Skip to content

Commit

Permalink
NamespacedId for type safety
Browse files Browse the repository at this point in the history
  • Loading branch information
j0code committed Feb 29, 2024
1 parent 344d775 commit ef1c337
Show file tree
Hide file tree
Showing 10 changed files with 39 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/Item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import ItemDef from "./defs/ItemDef.js"
import BlockDef from "./defs/BlockDef.js"
import { itemdefs, blockdefs } from "./main.js"
import Block from "./block/Block.js"
import { type HasData, type BaseData, type Flatten } from "./util/interfaces.js"
import { type HasData, type BaseData, type Flatten, type NamespacedId } from "./util/interfaces.js"

export default class Item implements HasData {

Expand All @@ -13,7 +13,7 @@ export default class Item implements HasData {

private readonly def: ItemDef | BlockDef

constructor(def: ItemDef | BlockDef | string) {
constructor(def: ItemDef | BlockDef | NamespacedId) {
if (def instanceof ItemDef || def instanceof BlockDef) this.def = def
else {
let itemdef = itemdefs.get(def) || blockdefs.get(def)
Expand Down
4 changes: 2 additions & 2 deletions src/block/Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Dim2 from "../dim/Dim2.js"
import Dim3 from "../dim/Dim3.js"
import { blockdefs, debug } from "../main.js"
import Graphics from "../Graphics.js"
import { type HasData, type BaseData, type Flatten } from "../util/interfaces.js"
import { type HasData, type BaseData, type Flatten, type NamespacedId } from "../util/interfaces.js"
import World from "../world/World.js"

export default class Block implements HasData {
Expand All @@ -18,7 +18,7 @@ export default class Block implements HasData {

private readonly light: { sky: number, block: number }

constructor(def: BlockDef | string) {
constructor(def: BlockDef | NamespacedId) {
if (def instanceof BlockDef) this.def = def
else {
let blockdef = blockdefs.get(def)
Expand Down
4 changes: 2 additions & 2 deletions src/block/ContainerBlock.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Inventory, { type InventoryData } from "../Inventory.js"
import BlockDef from "../defs/BlockDef.js"
import { type Flatten, type HasInventory } from "../util/interfaces.js"
import { type NamespacedId, type Flatten, type HasInventory } from "../util/interfaces.js"
import Block, { type BlockData } from "./Block.js"

export default class ContainerBlock extends Block implements HasInventory {

readonly inventory: Inventory

constructor(def: BlockDef | string, data: Partial<ContainerBlockData> = {}) {
constructor(def: BlockDef | NamespacedId, data: Partial<ContainerBlockData> = {}) {
super(def)
this.inventory = new Inventory(this.def.inventorySlots || 27, this.def.inventoryColumns || 9, data.items)
}
Expand Down
5 changes: 3 additions & 2 deletions src/defs/Base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Texture from "../texture/Texture.js"
import { getTexture } from "../main.js"
import { type NamespacedId } from "../util/interfaces.js";

export default class Base {
readonly namespace: string;
Expand All @@ -14,8 +15,8 @@ export default class Base {
else this.texture = getTexture(this.assetsPath)
}

get id() {
return this.namespace + ":" + this.idname
get id(): NamespacedId {
return `${this.namespace}:${this.idname}`
}

get assetsPath() {
Expand Down
4 changes: 2 additions & 2 deletions src/entity/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import EntityDef from "../defs/EntityDef.js"
import { entitydefs } from "../main.js"
import World from "../world/World.js"
import Graphics from "../Graphics.js"
import { type Flatten, type BaseData, type HasData } from "../util/interfaces.js"
import { type Flatten, type BaseData, type HasData, type NamespacedId } from "../util/interfaces.js"
import AttributeList from "../AttributeList.js"

export default class Entity implements HasData {
Expand Down Expand Up @@ -36,7 +36,7 @@ export default class Entity implements HasData {
onGround: boolean
inFluid: boolean

constructor(def: EntityDef | string, spawnTime: number, data: Partial<EntityData> = {}) {
constructor(def: EntityDef | NamespacedId, spawnTime: number, data: Partial<EntityData> = {}) {
if (def instanceof EntityDef) this.def = def
else {
let entitydef = entitydefs.get(def)
Expand Down
4 changes: 3 additions & 1 deletion src/entity/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Texture from "../texture/Texture.js"
import World from "../world/World.js"
import { type Flatten } from "../util/interfaces.js"

const playerDef = new PlayerDef()

export default class Player extends Entity {

private name: string
Expand All @@ -19,7 +21,7 @@ export default class Player extends Entity {
readonly hotbar: Inventory

constructor(skin: string, name: string, spawnTime: number, data: Partial<PlayerData> = {}) {
super(new PlayerDef(), spawnTime, { ...data, position: [0, 1, 0] })
super(playerDef, spawnTime, { ...data, position: [0, 1, 0] })
this.name = name
this.hotbar = data.hotbar ? new Inventory(5, 5, data.hotbar) : new Inventory(5)
this.skin = skin
Expand Down
9 changes: 5 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import DebugScreen from "./util/DebugScreen.js"
import CreativeInventory from "./CreativeInventory.js"
import Entity, { type EntityData } from "./entity/Entity.js"
import Item from "./Item.js"
import { type NamespacedId } from "./util/interfaces.js"

console.log("Never Gonna Give You Up")

Expand Down Expand Up @@ -108,9 +109,9 @@ function perfRun(name: "tick" | "draw", fn: Function, target: number) {
}
}

async function loadDefs<T>(path: string, cls: any): Promise<Map<String, T>> {
async function loadDefs<T>(path: string, cls: any): Promise<Map<NamespacedId, T>> {
let data = await YSON.load(path)
let defs = new Map<String, T>()
let defs = new Map<NamespacedId, T>()
let namespaces = Object.keys(data)

for (let ns of namespaces) {
Expand Down Expand Up @@ -417,7 +418,7 @@ export function getFirstFluid(world: World, x: number, y: number, startZ: number
return { block: undefined, z: world.minZ }
}

export function createBlock<T extends BlockData = BlockData>(id: string, data: Partial<T> = {}) {
export function createBlock<T extends BlockData = BlockData>(id: NamespacedId, data: Partial<T> = {}) {
const blockdef = blockdefs.get(id)
if (!blockdef) {
console.trace()
Expand All @@ -428,7 +429,7 @@ export function createBlock<T extends BlockData = BlockData>(id: string, data: P
else return new Block(blockdef)
}

export function createEntity<T extends EntityData = EntityData>(id: string, spawnTime: number, data: Partial<T> = {}) {
export function createEntity<T extends EntityData = EntityData>(id: NamespacedId, spawnTime: number, data: Partial<T> = {}) {
data.id = id
if (isItemEntityData(data, id)) return new ItemEntity(null, spawnTime, data)
else return new Entity(id, spawnTime, data)
Expand Down
7 changes: 4 additions & 3 deletions src/util/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Inventory from "../Inventory"
import ItemEntity from "../entity/ItemEntity"

export type ArrayElement<A> = A extends readonly (infer T)[] ? T : never

Expand All @@ -12,11 +11,13 @@ export interface HasInventory {
}

export type BaseData = {
id: string
id: NamespacedId
}

export interface HasData {
getData: (...args: any) => {}
}

export type DataOf<T extends HasData> = ReturnType<T["getData"]>
export type DataOf<T extends HasData> = ReturnType<T["getData"]>

export type NamespacedId = `${string}:${string}`
7 changes: 7 additions & 0 deletions src/util/typecheck.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { type NamespacedId } from "./interfaces";

export function isInteger(x: unknown): x is number {
return typeof x == "number" || Number.isInteger(x)
}

export const namespacedIdRegex = /^([a-z_]+):([a-z_]+)$/
export function isNamespacedId(s: string): s is NamespacedId {
return namespacedIdRegex.test(s)
}
12 changes: 9 additions & 3 deletions src/world/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import YSON from "https://j0code.github.io/browserjs-yson/main.mjs"
import { getFirstBlock } from "../main.js"
import Player, { type PlayerData } from "../entity/Player.js"
import { createBlock, createEntity } from "../main.js"
import { type NamespacedId } from "../util/interfaces.js"
import { isNamespacedId } from "../util/typecheck.js"

export default class World {

Expand All @@ -30,7 +32,11 @@ export default class World {
const semi = stringBlocks.indexOf(";")
if (!semi) return // invalid save

const blockIds = stringBlocks.substring(0, semi).split(",")
const blockIdStrings: string[] = stringBlocks.substring(0, semi).split(",")
const blockIds: NamespacedId[] = blockIdStrings.map(v => {
if (isNamespacedId(v)) return v
else return "tiny:air"
})
const blocks = Array.from(stringBlocks.substring(semi + 1)).map(v => v.charCodeAt(0))

const blockDataMap: Map<`${number},${number},${number}`, BlockData> = new Map()
Expand Down Expand Up @@ -105,7 +111,7 @@ export default class World {
return Array.from(this.blocks.values())
}

setBlock(x: number, y: number, z: number, block: Block | string, data?: Partial<BlockData>) {
setBlock(x: number, y: number, z: number, block: Block | NamespacedId, data?: Partial<BlockData>) {
const pos = this.validBlockPosition(x, y, z)
if (!pos) return
[x, y, z] = pos
Expand Down Expand Up @@ -153,7 +159,7 @@ export default class World {
return entities as E[]
}

spawn<T extends EntityData = EntityData>(entity: Entity | string, data?: Partial<T>) {
spawn<T extends EntityData = EntityData>(entity: Entity | NamespacedId, data?: Partial<T>) {
if (typeof entity == "string") {
this.entities.add(createEntity(entity, this.tickCount, data))
} else {
Expand Down

0 comments on commit ef1c337

Please sign in to comment.