prismarine-world 3.6.1 → 3.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,3 @@
1
- # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2
- # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
-
4
1
  name: CI
5
2
 
6
3
  on:
@@ -25,4 +22,4 @@ jobs:
25
22
  with:
26
23
  node-version: ${{ matrix.node-version }}
27
24
  - run: npm install
28
- - run: npm test
25
+ - run: npm test
@@ -13,13 +13,20 @@ jobs:
13
13
  - name: Set up Node.js
14
14
  uses: actions/setup-node@master
15
15
  with:
16
- node-version: 10.0.0
17
- - name: Publish if version has been updated
18
- uses: pascalgn/npm-publish-action@4f4bf159e299f65d21cd1cbd96fc5d53228036df
19
- with: # All of theses inputs are optional
20
- tag_name: "%s"
21
- tag_message: "%s"
22
- commit_pattern: "^Release (\\S+)"
23
- env: # More info about the environment variables in the README
24
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this as is, it's automatically generated
25
- NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} # You need to set this in your repo settings
16
+ node-version: 14.0.0
17
+ - id: publish
18
+ uses: JS-DevTools/npm-publish@v1
19
+ with:
20
+ token: ${{ secrets.NPM_AUTH_TOKEN }}
21
+ - name: Create Release
22
+ if: steps.publish.outputs.type != 'none'
23
+ id: create_release
24
+ uses: actions/create-release@v1
25
+ env:
26
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
+ with:
28
+ tag_name: ${{ steps.publish.outputs.version }}
29
+ release_name: Release ${{ steps.publish.outputs.version }}
30
+ body: ${{ steps.publish.outputs.version }}
31
+ draft: false
32
+ prerelease: false
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  [![Try it on gitpod](https://img.shields.io/badge/try-on%20gitpod-brightgreen.svg)](https://gitpod.io/#https://github.com/PrismarineJS/prismarine-world)
10
10
 
11
- Provides a minecraft world: an infinite collection of 16x256x26 chunks.
11
+ Provides a minecraft world: an infinite collection of 16x256x16 chunks.
12
12
 
13
13
  ## Usage
14
14
 
package/docs/HISTORY.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## History
2
2
 
3
+ ### 3.6.2
4
+
5
+ * Fix chunk saving reference errors (@kf106 & @moonborrow) [#100](https://github.com/PrismarineJS/prismarine-world/pull/100)
6
+ * Fix typo in README.md (@takeru) [#108](https://github.com/PrismarineJS/prismarine-world/pull/108)
7
+ * Improved types (@TRCYX) [#99](https://github.com/PrismarineJS/prismarine-world/pull/99)
8
+ * Fix ManhattanIterator [#101](https://github.com/PrismarineJS/prismarine-world/pull/101) (@IceTank)
9
+
3
10
  ### 3.6.1
4
11
 
5
12
  * Update mcdata
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "prismarine-world",
3
- "version": "3.6.1",
3
+ "version": "3.6.2",
4
4
  "description": "The core implementation of the world for prismarine",
5
5
  "main": "index.js",
6
+ "types": "./types/index.d.ts",
6
7
  "scripts": {
7
8
  "pretest": "npm run lint",
8
9
  "lint": "standard",
@@ -32,19 +33,19 @@
32
33
  "homepage": "https://github.com/PrismarineJS/prismarine-world",
33
34
  "devDependencies": {
34
35
  "buffer-equal": "^1.0.0",
36
+ "expect": "^29.1.0",
35
37
  "flatmap": "0.0.3",
36
38
  "minecraft-data": "^3.0.0",
37
39
  "mkdirp": "^0.5.1",
38
- "prismarine-chunk": "^1.14.0",
39
- "prismarine-world": "file:.",
40
+ "mocha": "^10.0.0",
41
+ "prismarine-chunk": "^1.31.0",
40
42
  "prismarine-provider-anvil": "^2.0.0",
41
43
  "prismarine-provider-raw": "^1.0.1",
44
+ "prismarine-world": "file:.",
42
45
  "process": "^0.11.0",
43
46
  "range": "0.0.3",
44
47
  "rimraf": "^3.0.2",
45
- "standard": "^16.0.1",
46
- "expect": "^27.3.1",
47
- "mocha": "^9.1.3"
48
+ "standard": "^17.0.0"
48
49
  },
49
50
  "dependencies": {
50
51
  "vec3": "^0.1.7"
package/src/iterators.js CHANGED
@@ -24,11 +24,15 @@ class ManhattanIterator {
24
24
  this.leg = -1
25
25
  }
26
26
 
27
+ /**
28
+ * Returns the next position. If the iterator is at the end, returns null. Position will be 2d along the x z plane and y always being 0.
29
+ * @returns {Vec3 | null}
30
+ */
27
31
  next () {
28
32
  if (this.leg === -1) {
29
33
  // use -1 as the center
30
34
  this.leg = 0
31
- return { x: this.startx, y: this.starty }
35
+ return new Vec3(this.startx, 0, this.starty)
32
36
  } else if (this.leg === 0) {
33
37
  if (this.maxDistance === 1) return null
34
38
  this.x--
@@ -217,7 +221,7 @@ class SpiralIterator2d {
217
221
  * 20 21 22 23 24
218
222
  * (maxDistance = 2; points returned = 25)
219
223
  * ```
220
- * Copy and past warrior source: https://stackoverflow.com/questions/3706219/algorithm-for-iterating-over-an-outward-spiral-on-a-discrete-2d-grid-from-the-or
224
+ * Copy and paste warrior source: https://stackoverflow.com/questions/3706219/algorithm-for-iterating-over-an-outward-spiral-on-a-discrete-2d-grid-from-the-or
221
225
  * @param {Vec3} pos Starting position
222
226
  * @param {number} maxDistance Max distance from starting position
223
227
  */
package/src/world.js CHANGED
@@ -16,7 +16,9 @@ class World extends EventEmitter {
16
16
  constructor (chunkGenerator, storageProvider = null, savingInterval = 1000) {
17
17
  super()
18
18
  this.savingQueue = new Map()
19
+ this.unloadQueue = new Map()
19
20
  this.finishedSaving = Promise.resolve()
21
+ this.currentlySaving = false // semaphore for saving
20
22
  this.columns = {}
21
23
  this.chunkGenerator = chunkGenerator
22
24
  this.storageProvider = storageProvider
@@ -123,6 +125,17 @@ class World extends EventEmitter {
123
125
 
124
126
  unloadColumn (chunkX, chunkZ) {
125
127
  const key = columnKeyXZ(chunkX, chunkZ)
128
+ if (this.storageProvider && this.savingQueue.has(key)) {
129
+ this.unloadQueue.set(key, { chunkX, chunkZ })
130
+ } else {
131
+ this.forceUnloadColumn(key, chunkX, chunkZ)
132
+ }
133
+ }
134
+
135
+ forceUnloadColumn (key, chunkX, chunkZ) {
136
+ if (this.unloadQueue.has(key)) {
137
+ this.unloadQueue.delete(key)
138
+ }
126
139
  delete this.columns[key]
127
140
  const columnCorner = new Vec3(chunkX * 16, 0, chunkZ * 16)
128
141
  this.emit('chunkColumnUnload', columnCorner)
@@ -136,15 +149,24 @@ class World extends EventEmitter {
136
149
  // interval. The set structure is maintaining the order of insertion
137
150
  for (const [key, { chunkX, chunkZ }] of this.savingQueue.entries()) {
138
151
  this.finishedSaving = Promise.all([this.finishedSaving,
139
- this.storageProvider.save(chunkX, chunkZ, this.columns[key])])
152
+ this.storageProvider.save(chunkX, chunkZ, this.columns[key])
153
+ ])
140
154
  }
155
+ await this.finishedSaving
141
156
  this.savingQueue.clear()
157
+ for (const [key, { chunkX, chunkZ }] of this.unloadQueue.entries()) {
158
+ this.forceUnloadColumn(key, chunkX, chunkZ)
159
+ }
142
160
  this.emit('doneSaving')
143
161
  }
144
162
 
145
163
  startSaving () {
146
164
  this.savingInt = setInterval(async () => {
147
- await this.saveNow()
165
+ if (this.currentlySaving === false) {
166
+ this.currentlySaving = true
167
+ await this.saveNow()
168
+ this.currentlySaving = false
169
+ }
148
170
  }, this.savingInterval)
149
171
  }
150
172
 
@@ -1,8 +1,8 @@
1
1
  /* eslint-env mocha */
2
2
 
3
- const { SpiralIterator2d } = require('../src/iterators')
3
+ const { SpiralIterator2d, ManhattanIterator } = require('../src/iterators')
4
4
  const { Vec3 } = require('vec3')
5
- const expect = require('expect')
5
+ const expect = require('expect').default
6
6
 
7
7
  describe('Spiral iterator', () => {
8
8
  it('simple function test', async () => {
@@ -15,3 +15,25 @@ describe('Spiral iterator', () => {
15
15
  expect(second.x === startPos.x && second.y === startPos.y && second.z === startPos.z).toBeFalsy()
16
16
  })
17
17
  })
18
+
19
+ describe('ManhattanIterator iterator', () => {
20
+ it('First position is same as start', () => {
21
+ const start = new Vec3(1, 2, 3)
22
+ const iter = new ManhattanIterator(start.x, start.z, 5)
23
+ const first = iter.next()
24
+ expect(first.x === start.x && first.z === start.z).toBeTruthy()
25
+ })
26
+
27
+ it('Sample positions match', () => {
28
+ const start = new Vec3(1, 2, 3)
29
+ const iter = new ManhattanIterator(start.x, start.z, 5)
30
+ const sample = [new Vec3(1, 0, 3), new Vec3(2, 0, 3), new Vec3(1, 0, 4), new Vec3(0, 0, 3)]
31
+ let counter = 0
32
+ let next = iter.next()
33
+ while (next && counter < sample.length) {
34
+ expect(next.x === sample[counter].x && next.z === sample[counter].z).toBeTruthy()
35
+ next = iter.next()
36
+ counter++
37
+ }
38
+ })
39
+ })
@@ -0,0 +1,10 @@
1
+ import { World } from "./world";
2
+
3
+ export * as iterators from "./iterators";
4
+
5
+ // Make public the types related to World, but not the classes
6
+ // (since they aren't exported in js)
7
+ import type * as world from "./world";
8
+ export { world };
9
+
10
+ export default function loader(mcVersion: string): typeof World;
@@ -0,0 +1,145 @@
1
+ import type { Vec3 } from "vec3";
2
+
3
+ export declare enum BlockFace {
4
+ UNKNOWN = -999,
5
+ BOTTOM = 0,
6
+ TOP = 1,
7
+ NORTH = 2,
8
+ SOUTH = 3,
9
+ WEST = 4,
10
+ EAST = 5,
11
+ }
12
+
13
+ export type RaycastBlock = {
14
+ x: number;
15
+ y: number;
16
+ z: number;
17
+ face: BlockFace;
18
+ };
19
+
20
+ export type RaycastIntersection = {
21
+ pos: Vec3;
22
+ face: BlockFace;
23
+ };
24
+
25
+ /** [x0, y0, z0, x1, y1, z1] */
26
+ export type Shape = [number, number, number, number, number, number];
27
+
28
+ export interface Iterator<T> {
29
+ next(): T | null;
30
+ }
31
+
32
+ // 2D spiral iterator, useful to iterate on
33
+ // columns that are centered on bot position
34
+ // https://en.wikipedia.org/wiki/Taxicab_geometry
35
+ export declare class ManhattanIterator implements Iterator<Vec3> {
36
+ public readonly maxDistance: number;
37
+ public readonly startx: number;
38
+ public readonly starty: number;
39
+ private x: number;
40
+ private y: number;
41
+ private layer: number;
42
+ private leg: number;
43
+
44
+ constructor(x: number, y: number, maxDistance: number);
45
+
46
+ /**
47
+ * @returns {Vec3 | null} The current position of the iterator OR null if exceeded maxDistance.
48
+ */
49
+ next(): Vec3 | null;
50
+ }
51
+
52
+ export declare class OctahedronIterator implements Iterator<Vec3> {
53
+ public readonly start: Vec3;
54
+ public readonly maxDistance: number;
55
+ private apothem: number;
56
+ private x: number;
57
+ private y: number;
58
+ private z: number;
59
+ private L: number;
60
+ private R: number;
61
+
62
+ constructor(start: Vec3, maxDistance: number);
63
+
64
+ /**
65
+ * @returns {Vec3 | null} The current position of the iterator OR null if exceeded maxDistance.
66
+ */
67
+ next(): Vec3 | null;
68
+ }
69
+
70
+ // This iterate along a ray starting at `pos` in `dir` direction
71
+ // It steps exactly 1 block at a time, returning the block coordinates
72
+ // and the face by which the ray entered the block.
73
+ export declare class RaycastIterator implements Iterator<RaycastBlock> {
74
+ private block: RaycastBlock;
75
+ public readonly pos: Vec3;
76
+ public readonly dir: Vec3;
77
+ private invDirX: number;
78
+ private invDirY: number;
79
+ private invDirZ: number;
80
+ private stepX: number;
81
+ private stepY: number;
82
+ private stepZ: number;
83
+ private tDeltaX: number;
84
+ private tDeltaY: number;
85
+ private tDeltaZ: number;
86
+ private tMaxX: number;
87
+ private tMaxY: number;
88
+ private tMaxZ: number;
89
+ public readonly maxDistance: number;
90
+
91
+ constructor(pos: Vec3, dir: Vec3, distance: number);
92
+
93
+ /**
94
+ * @param {Shape[]} shapes Takes shapes from block.shapes.
95
+ * @param {Vec3} offset Offset from original position to start the intersect calculation from.
96
+ * @returns {RaycastIntersection | null} A possible intersection.
97
+ */
98
+ intersect(shapes: Shape[], offset: Vec3): RaycastIntersection | null;
99
+
100
+ /**
101
+ * @returns {RaycastBlock | null} The block the raycast is currently passing through (not hit detection, just the general AABB of a block if it were there).
102
+ */
103
+ next(): RaycastBlock | null;
104
+ }
105
+
106
+ /**
107
+ * Spiral outwards from a central position in growing squares.
108
+ * Every point has a constant distance to its previous and following position of 1. First point returned is the starting position.
109
+ * Generates positions like this:
110
+ * ```text
111
+ * 16 15 14 13 12
112
+ * 17 4 3 2 11
113
+ * 18 5 0 1 10
114
+ * 19 6 7 8 9
115
+ * 20 21 22 23 24
116
+ * (maxDistance = 2; points returned = 25)
117
+ * ```
118
+ * Copy and paste warrior source: https://stackoverflow.com/questions/3706219/algorithm-for-iterating-over-an-outward-spiral-on-a-discrete-2d-grid-from-the-or
119
+ */
120
+ export declare class SpiralIterator2d implements Iterator<Vec3> {
121
+ public readonly start: Vec3;
122
+ public readonly maxDistance: number;
123
+ private di: number;
124
+ private dj: number;
125
+ private i: number;
126
+ private j: number;
127
+ private k: number;
128
+ private segment_passed: number;
129
+ private segment_length: number;
130
+ private readonly NUMBER_OF_POINTS: number;
131
+
132
+ /**
133
+ * @param {Vec3} pos Starting position
134
+ * @param {number} maxDistance Max distance from starting position
135
+ */
136
+ constructor(pos: Vec3, maxDistance: number);
137
+
138
+
139
+ /**
140
+ * @returns {Vec3 | null} The current position of the iterator OR null if exceeded maxDistance.
141
+ */
142
+ next(): Vec3 | null;
143
+ }
144
+
145
+ export declare const ManathanIterator: ManhattanIterator;
@@ -0,0 +1,215 @@
1
+ import type { EventEmitter } from "events";
2
+ import type { Vec3 } from "vec3";
3
+ import type { Block } from "prismarine-block";
4
+ import loaderOfChunk from "prismarine-chunk";
5
+ import type { RaycastBlock } from "./iterators";
6
+
7
+ export type Chunk = InstanceType<ReturnType<typeof loaderOfChunk>>;
8
+
9
+ export type ChunkGenerator = (chunkX: number, chunkZ: number) => Chunk;
10
+ export type ChunkCoordinates = { chunkX: number, chunkZ: number };
11
+ export type ChunkCoordsAndColumn = { chunkX: number, chunkZ: number, column: Chunk };
12
+
13
+ export interface StorageProvider {
14
+ load(chunkX: number, chunkZ: number): Promise<Chunk>;
15
+ save(chunkX: number, chunkZ: number, chunk: Chunk): Promise<void>;
16
+ }
17
+
18
+ export declare class World extends EventEmitter {
19
+
20
+ private readonly savingQueue: Map<String, ChunkCoordinates>;
21
+ private finishedSaving: Promise<unknown>;
22
+ private readonly columns: { [key: string]: Chunk };
23
+ public readonly chunkGenerator: ChunkGenerator | null | undefined;
24
+ public readonly storageProvider: StorageProvider | null | undefined;
25
+ public readonly savingInterval: number;
26
+ public readonly sync: WorldSync;
27
+
28
+ /**
29
+ * @param {ChunkGenerator} chunkGenerator A generator that requires X and Z coordinates of lower left of chunk. Returns a {Chunk} chunk object.
30
+ * @param {StorageProvider} storageProvider The provider the world uses to save chunks. See "prismarine-provider-anvil" or "prismarine-provider-raw".
31
+ * @param {number} savingInterval The duration in milliseconds between saves.
32
+ */
33
+ constructor(
34
+ chunkGenerator?: ChunkGenerator | null | undefined,
35
+ storageProvider?: StorageProvider | null | undefined,
36
+ savingInterval?: number,
37
+ );
38
+
39
+ public initialize(
40
+ iniFunc: (x: number, y: number, z: number) => Block,
41
+ length: number,
42
+ width: number,
43
+ height?: number,
44
+ iniPos?: Vec3,
45
+ ): Promise<ChunkCoordinates[]>;
46
+
47
+ /**
48
+ *
49
+ * @param {Vec3} from Position to start raycasting from.
50
+ * @param {Vec3} direction Normalized vector, direction of raycast.
51
+ * @param {number} range Maximum range to raycast.
52
+ * @param {(block: Block) => boolean} matcher Optional function with a block parameter, return true if the raycast should stop at this block, false otherwise.
53
+ */
54
+ public raycast(
55
+ from: Vec3,
56
+ direction: Vec3,
57
+ range: number,
58
+ matcher?: (block: Block) => boolean,
59
+ ): Promise<RaycastBlock | null>;
60
+
61
+ public getLoadedColumn(chunkX: number, chunkZ: number): Chunk;
62
+
63
+ public getColumn(chunkX: number, chunkZ: number): Promise<Chunk>;
64
+
65
+ private _emitBlockUpdate(
66
+ oldBlock: Block,
67
+ newBlock: Block,
68
+ position: Vec3,
69
+ ): void;
70
+
71
+ public setLoadedColumn(
72
+ chunkX: number,
73
+ chunkZ: number,
74
+ chunk: Chunk,
75
+ save?: boolean,
76
+ ): void;
77
+
78
+ public setColumn(
79
+ chunkX: number,
80
+ chunkZ: number,
81
+ chunk: Chunk,
82
+ save?: boolean,
83
+ ): Promise<void>;
84
+
85
+ public unloadColumn(chunkX: number, chunkZ: number): void;
86
+
87
+ public saveNow(): Promise<void>;
88
+
89
+ public startSaving(): void;
90
+
91
+ public waitSaving(): Promise<void>;
92
+
93
+ public stopSaving(): void;
94
+
95
+ public queueSaving(chunkX: number, chunkZ: number): void;
96
+
97
+ public saveAt(pos: Vec3): void;
98
+
99
+ public getColumns(): ChunkCoordsAndColumn[];
100
+
101
+ public getLoadedColumnAt(pos: Vec3): Chunk;
102
+
103
+ public getColumnAt(pos: Vec3): Promise<Chunk>;
104
+
105
+ public setBlock(pos: Vec3, block: Block): Promise<void>;
106
+
107
+ public getBlock(pos: Vec3): Promise<Block>;
108
+
109
+ public getBlockStateId(pos: Vec3): Promise<number>;
110
+
111
+ public getBlockType(pos: Vec3): Promise<number>;
112
+
113
+ public getBlockData(pos: Vec3): Promise<number>;
114
+
115
+ public getBlockLight(pos: Vec3): Promise<number>;
116
+
117
+ public getSkyLight(pos: Vec3): Promise<number>;
118
+
119
+ public getBiome(pos: Vec3): Promise<number>;
120
+
121
+ public setBlockStateId(pos: Vec3, stateId: number): Promise<void>;
122
+
123
+ public setBlockType(pos: Vec3, blockType: number): Promise<void>;
124
+
125
+ public setBlockData(pos: Vec3, data: number): Promise<void>;
126
+
127
+ public setBlockLight(pos: Vec3, light: number): Promise<void>;
128
+
129
+ public setSkyLight(pos: Vec3, light: number): Promise<void>;
130
+
131
+ public setBiome(pos: Vec3, biome: number): Promise<void>;
132
+
133
+ }
134
+
135
+ export declare class WorldSync extends EventEmitter {
136
+
137
+ public readonly async: World;
138
+
139
+ /**
140
+ * @param {World} world Async representation of the World object.
141
+ */
142
+ constructor(world: World);
143
+
144
+
145
+ public initialize(
146
+ iniFunc: (x: number, y: number, z: number) => Block,
147
+ length: number,
148
+ width: number,
149
+ height?: number,
150
+ iniPos?: Vec3,
151
+ ): void;
152
+
153
+ /**
154
+ *
155
+ * @param {Vec3} from Position to start raycasting from.
156
+ * @param {Vec3} direction Normalized vector, direction of raycast.
157
+ * @param {number} range Maximum range to raycast.
158
+ * @param {(block: Block) => boolean} matcher Optional function with a block parameter, return true if the raycast should stop at this block, false otherwise.
159
+ */
160
+ public raycast(
161
+ from: Vec3,
162
+ direction: Vec3,
163
+ range: number,
164
+ matcher?: (block: Block) => boolean,
165
+ ): RaycastBlock | null;
166
+
167
+ private _emitBlockUpdate(
168
+ oldBlock: Block,
169
+ newBlock: Block,
170
+ position: Vec3
171
+ ): void;
172
+
173
+ public getColumns(): ChunkCoordsAndColumn[];
174
+
175
+ public getColumn(chunkX: number, chunkZ: number): Chunk;
176
+
177
+ public getColumnAt(pos: Vec3): Chunk;
178
+
179
+ public setColumn(
180
+ chunkX: number,
181
+ chunkZ: number,
182
+ chunk: Chunk,
183
+ save?: boolean,
184
+ ): void;
185
+
186
+ public unloadColumn(chunkX: number, chunkZ: number): void;
187
+
188
+ public getBlock(pos: Vec3): Block;
189
+
190
+ public getBlockStateId(pos: Vec3): number;
191
+
192
+ public getBlockType(pos: Vec3): number;
193
+
194
+ public getBlockData(pos: Vec3): number;
195
+
196
+ public getBlockLight(pos: Vec3): number;
197
+
198
+ public getSkyLight(pos: Vec3): number;
199
+
200
+ public getBiome(pos: Vec3): number;
201
+
202
+ public setBlock(pos: Vec3, block: Block): void;
203
+
204
+ public setBlockStateId(pos: Vec3, stateId: number): void;
205
+
206
+ public setBlockType(pos: Vec3, blockType: number): void;
207
+
208
+ public setBlockData(pos: Vec3, data: number): void;
209
+
210
+ public setBlockLight(pos: Vec3, light: number): void;
211
+
212
+ public setSkyLight(pos: Vec3, light: number): void;
213
+
214
+ public setBiome(pos: Vec3, biome: number): void;
215
+ }