Skip to content

Commit 768b69b

Browse files
zardoyrom1504
andauthored
Refactor: allow usage in browser (#633)
* Refactor: allow usage in browser * move method to player * fix bad require * fix standard * add basic tsconfig * remove unused dep: didn't notice because of packages resorted in d * [skip ci] rm comment * remove mocking remoteAddress * make event: playerChangeRenderDistance, up docs * introduce pluginsReady prop + event * revert auto-fix changes * doc player.save * dont init player on its disconnect * seems got the problem * [skip ci] remove server.options * don't mock socket * add serv.formatMessage --------- Co-authored-by: Romain Beaumont <[email protected]>
1 parent c1d381e commit 768b69b

File tree

16 files changed

+585
-449
lines changed

16 files changed

+585
-449
lines changed

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
package-lock=false
2+
public-hoist-pattern=*

docs/API.md

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@
1818
- [MCServer](#mcserver)
1919
- [Flying-squid.createMCServer(options)](#flying-squidcreatemcserveroptions)
2020
- [Properties](#properties)
21+
- [serv.pluginsReady](#servpluginsready)
2122
- [serv.entityMaxId](#serventitymaxid)
2223
- [serv.players](#servplayers)
2324
- [serv.uuidToPlayer](#servuuidtoplayer)
2425
- [serv.overworld](#servoverworld)
2526
- [serv.netherworld](#servnetherworld)
26-
- [serv.endworld](#servendworld)
2727
- [serv.entities](#serventities)
2828
- [serv.bannedPlayers](#servbannedplayers)
2929
- [serv.time](#servtime)
3030
- [serv.tickCount](#servtickcount)
3131
- [serv.doDaylightCycle](#servdodaylightcycle)
3232
- [serv.plugins](#servplugins)
33+
- [serv.commands](#servcommands)
3334
- [serv.tabComplete](#servtabcomplete)
3435
- [Events](#events)
3536
- ["error" (error)](#error-error)
@@ -38,7 +39,9 @@
3839
- ["newPlayer" (player)](#newplayer-player)
3940
- ["banned" (banner,bannedUsername,reason)](#banned-bannerbannedusernamereason)
4041
- ["tick" (count)](#tick-count)
42+
- ["pluginsReady"](#pluginsready)
4143
- [Methods](#methods)
44+
- [serv.formatMessage(message)](#servformatmessagemessage)
4245
- [serv.createLog()](#servcreatelog)
4346
- [serv.log(message)](#servlogmessage)
4447
- [serv.info(message)](#servinfomessage)
@@ -66,12 +69,13 @@
6669
- [server.playNoteBlock(world, position, pitch)](#serverplaynoteblockworld-position-pitch)
6770
- [server.getNote(note)](#servergetnotenote)
6871
- [server.emitParticle(particle, world, position, opt)](#serveremitparticleparticle-world-position-opt)
72+
- [serv.selectorString(str, pos, world, allowUser = true, ctxEntityId)](#servselectorstringstr-pos-world-allowuser--true-ctxentityid)
6973
- [Low level methods](#low-level-methods)
7074
- [server._writeAll(packetName, packetFields)](#server_writeallpacketname-packetfields)
7175
- [server._writeArray(packetName, packetFields, playerArray)](#server_writearraypacketname-packetfields-playerarray)
7276
- [server._writeNearby(packetName, packetFields, loc)](#server_writenearbypacketname-packetfields-loc)
73-
- [serv._loadPlayerChunk(chunkX, chunkZ, player)](#serv_loadplayerchunk)
74-
- [serv._unloadPlayerChunk(chunkX, chunkZ, player)](#serv_unloadplayerchunk)
77+
- [serv._loadPlayerChunk(chunkX, chunkZ, player)](#serv_loadplayerchunkchunkx-chunkz-player)
78+
- [serv._unloadPlayerChunk(chunkX, chunkZ, player)](#serv_unloadplayerchunkchunkx-chunkz-player)
7579
- [Entity](#entity-1)
7680
- [Properties](#properties-1)
7781
- [entity.id](#entityid)
@@ -125,6 +129,8 @@
125129
- ["disconnected"](#disconnected)
126130
- ["chat" (message)](#chat-message)
127131
- ["kicked" (kicker,reason)](#kicked-kickerreason)
132+
- ["change_world"](#change_world)
133+
- ["playerChangeRenderDistance" (newDistance=player.view, unloadFirst=false)](#playerchangerenderdistance-newdistanceplayerview-unloadfirstfalse)
128134
- ["positionChanged"](#positionchanged)
129135
- [Behaviors](#behaviors-1)
130136
- ["move"](#move-1)
@@ -144,6 +150,7 @@
144150
- ["attack"](#attack)
145151
- ["requestRespawn"](#requestrespawn)
146152
- [Methods](#methods-2)
153+
- [player.save()](#playersave)
147154
- [player.login()](#playerlogin)
148155
- [player.ban(reason)](#playerbanreason)
149156
- [player.kick(reason)](#playerkickreason)
@@ -152,14 +159,15 @@
152159
- [player.changeBlock(position,blockType,blockData)](#playerchangeblockpositionblocktypeblockdata)
153160
- [player.sendBlock(position,blockType,blockData)](#playersendblockpositionblocktypeblockdata)
154161
- [player.sendBlockAction(position,actionId,actionParam,blockType)](#playersendblockactionpositionactionidactionparamblocktype)
162+
- [player.sendBrand(brand = 'flying-squid')](#playersendbrandbrand--flying-squid)
155163
- [player.sendInitialPosition()](#playersendinitialposition)
156164
- [player.setGameMode(gameMode)](#playersetgamemodegamemode)
157165
- [player.handleCommand(command)](#playerhandlecommandcommand)
158166
- [player.setBlock(position,blockType,blockData)](#playersetblockpositionblocktypeblockdata)
159167
- [player.setBlockAction(position,actionId,actionParam)](#playersetblockactionpositionactionidactionparam)
160168
- [player.updateHealth(health)](#playerupdatehealthhealth)
161-
- [player.updateFood(health)](#playerupdatefoodfood)
162-
- [player.updateFoodSaturation(health)](#playerupdatefoodsaturationfoodsaturation)
169+
- [player.updateFood(food)](#playerupdatefoodfood)
170+
- [player.updateFoodSaturation(foodSaturation)](#playerupdatefoodsaturationfoodsaturation)
163171
- [player.changeWorld(world, opt)](#playerchangeworldworld-opt)
164172
- [player.spawnAPlayer(spawnedPlayer)](#playerspawnaplayerspawnedplayer)
165173
- [player.updateAndSpawnNearbyPlayers()](#playerupdateandspawnnearbyplayers)
@@ -171,8 +179,8 @@
171179
- [Low level properties](#low-level-properties)
172180
- [player._client](#player_client)
173181
- [Low level methods](#low-level-methods-1)
174-
- [player._unloadChunk(chunkX, chunkZ)](#player_unloadchunk)
175-
- [player._unloadAllChunks](#player_unloadallchunks)
182+
- [player._unloadChunk(chunkX, chunkZ)](#player_unloadchunkchunkx-chunkz)
183+
- [player._unloadAllChunks()](#player_unloadallchunks)
176184

177185
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
178186

@@ -254,6 +262,10 @@ Options is an object containing the following properties:
254262

255263
### Properties
256264

265+
#### serv.pluginsReady
266+
267+
`true` if all async plugins are ready and the server is ready to accept connections.
268+
257269
#### serv.entityMaxId
258270

259271
The current maximum ID (i.e. the last entity that was spawned has that id)
@@ -321,7 +333,7 @@ serv.commands.add({
321333
parse(str) {
322334
const args = str.split(' ');
323335
if(args.length != 1) return false;
324-
336+
325337
return {pseudo:args[0]};
326338
},
327339
action({pseudo}, ctx) {
@@ -368,8 +380,16 @@ Fires when `player` login, allow external player plugins.
368380

369381
Fires when one tick has passed (default is 50ms). count is the total world ticks (same as serv.tickCount)
370382

383+
#### "pluginsReady"
384+
385+
Emitted when `serv.pluginsReady` is set to `true`.
386+
371387
### Methods
372388

389+
#### serv.formatMessage(message)
390+
391+
You can override this function so you can process the message before sending it to the console.
392+
373393
#### serv.createLog()
374394

375395
Creates the log file
@@ -478,7 +498,7 @@ Sends a block action to all players of the same world.
478498

479499
#### server.playSound(sound, world, position, opt)
480500

481-
Plays `sound` (string, google "minecraft sound list") to all players in `opt.radius`.
501+
Plays `sound` (string, google "minecraft sound list") to all players in `opt.radius`.
482502
If position is null, will play at the location of every player (taking into account whitelist and blacklist).
483503

484504
Opt:
@@ -644,16 +664,16 @@ List of entities that the entity believes is nearby.
644664

645665
Behaviors are very interesting. Let me explain to you how they work:
646666

647-
Behaviors are a special type of event. They are editable and allow defaults to be cancellable making the powerful
648-
for plugins to take control of and interact with each other. Three different events get called
667+
Behaviors are a special type of event. They are editable and allow defaults to be cancellable making the powerful
668+
for plugins to take control of and interact with each other. Three different events get called
649669
for a behavior:
650670
- EVENTNAME_cancel
651671
- EVENTNAME
652672
- EVENTNAME_done
653673

654674
EVENTNAME_cancel passses the paramaters `data` (object of all info about behavior. Changing the data could have effects on outcome) and `cancel`, a function. This event is run before the default action. If `cancel()` is called, it will cancel the default action. More on this later.
655675

656-
EVENTNAME passes `data` as well as `cancelled` so plugins can check if the default behavior has been cancelled. This is event is run
676+
EVENTNAME passes `data` as well as `cancelled` so plugins can check if the default behavior has been cancelled. This is event is run
657677
before the default action.
658678

659679
EVENTNAME_done passes `data` and `cancelled`. This event is run before the default action.
@@ -674,9 +694,9 @@ player.on('move', ({position}, cancelled) => {
674694
})
675695
```
676696

677-
When a player normally moves, the server saves their position and sends it to all clients. Therefore, if a "move" behavior was truly cancelled,
678-
the player would be able to move freely while the server and other players would see the player stationary. This doesn't happen because
679-
behaviors can have "default cancel functions". In the case of a player's "move", the default cancel function sends them back where they
697+
When a player normally moves, the server saves their position and sends it to all clients. Therefore, if a "move" behavior was truly cancelled,
698+
the player would be able to move freely while the server and other players would see the player stationary. This doesn't happen because
699+
behaviors can have "default cancel functions". In the case of a player's "move", the default cancel function sends them back where they
680700
came from. To prevent this from happening, use the "preventDefaultCancel" paramater: cancel(false);
681701

682702
Plugin C
@@ -686,10 +706,10 @@ player.on('move_cancel', ({position}, cancel) => {
686706
});
687707
```
688708

689-
If we keep Plugin B and replace Plugin A with Plugin C, we'll see that the player can move freely but will not receive the
709+
If we keep Plugin B and replace Plugin A with Plugin C, we'll see that the player can move freely but will not receive the
690710
word "HI" and other players will be unable to see their movements.
691711

692-
Finally, there is hidden cancel. This is the second parameter in cancel, and allows plugins to hide the fact that they cancelled
712+
Finally, there is hidden cancel. This is the second parameter in cancel, and allows plugins to hide the fact that they cancelled
693713
the default action from other plugins. It's best not to use this, but I know somebody will someday need this.
694714

695715
Plugin D
@@ -699,7 +719,7 @@ player.on('move_cancel', ({position}, cancel) => {
699719
})
700720
```
701721

702-
Using Plugin B and D together, the player will be able to move freely and will be spammed with "HI", however the server will not store
722+
Using Plugin B and D together, the player will be able to move freely and will be spammed with "HI", however the server will not store
703723
their position and other players will not see the player move.
704724

705725
#### FORMAT
@@ -794,7 +814,7 @@ Level of xp the player has. Set this with player.setXpLevel()
794814

795815
### Events
796816

797-
#### "connected"
817+
#### "connected"
798818

799819
Fires when the player is connected
800820

@@ -814,6 +834,14 @@ Fires when the player says `message`.
814834

815835
`kicker` kick the player with `reason`
816836

837+
#### "change_world"
838+
839+
World of the player has been changed.
840+
841+
#### "playerChangeRenderDistance" (newDistance=player.view, unloadFirst=false)
842+
843+
Emit this event to change player render distance.
844+
817845
#### "positionChanged"
818846

819847
fires when the position changes in small amounts (walking, running, or flying)
@@ -1004,6 +1032,17 @@ Cancelled: Nothing. You monster.
10041032

10051033
### Methods
10061034

1035+
#### player.save()
1036+
1037+
If `worldFolder` option is set, save player's data into `<worldFolder>/playerdata/<UUID>.dat`. Returns promise.
1038+
Example: save all players data to disk:
1039+
1040+
```js
1041+
for (const player of serv.players) {
1042+
player.save()
1043+
}
1044+
```
1045+
10071046
#### player.login()
10081047

10091048
login
@@ -1028,7 +1067,7 @@ sends `message` to the player
10281067

10291068
change the block at position `position` to `blockType` and `blockData`
10301069

1031-
this will not change the block for the user himself. It is mainly useful when a user places a block
1070+
this will not change the block for the user himself. It is mainly useful when a user places a block
10321071
and only needs to send it to other players on the server
10331072

10341073
#### player.sendBlock(position,blockType,blockData)
@@ -1041,7 +1080,7 @@ this will not make any changes on the server's world and only sends it to the us
10411080

10421081
Set the block action at position `position` to `actionId` and `actionParam`.
10431082

1044-
``blockType`` is only required when the block at the location is a fake block.
1083+
``blockType`` is only required when the block at the location is a fake block.
10451084
This will only be caused by using ``player.sendBlock``.
10461085

10471086
This will not make any changes to the server's world and only sends it to the user as a local action.

package.json

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
"email": "[email protected]"
1111
}
1212
],
13-
"bin": {
14-
"flying-squid": "app.js"
15-
},
13+
"bin": "app.js",
1614
"scripts": {
1715
"prepublishOnly": "cp docs/README.md README.md",
1816
"lint": "standard test/*.test.js src/**/*.js src/**/**/*.js src/*.js examples/*.js *.js",
@@ -21,7 +19,11 @@
2119
"test": "npm run mocha_test",
2220
"pretest": "npm run lint"
2321
},
24-
"keywords": [],
22+
"keywords": [
23+
"browser",
24+
"minecraft server",
25+
"java minecraft server"
26+
],
2527
"license": "MIT",
2628
"engines": {
2729
"node": ">=8"
@@ -32,21 +34,21 @@
3234
"diamond-square": "^1.2.0",
3335
"emit-then": "^2.0.0",
3436
"event-promise": "^0.0.1",
37+
"exit-hook": "^2.2.1",
3538
"flatmap": "^0.0.3",
3639
"long": "^5.1.0",
37-
"minecraft-data": "^3.0.0",
38-
"minecraft-protocol": "^1.15.0",
39-
"mkdirp": "^2.1.3",
40+
"minecraft-data": "^3.42.1",
41+
"minecraft-protocol": "^1.44.0",
4042
"moment": "^2.10.6",
4143
"needle": "^2.5.0",
4244
"node-gzip": "^1.1.2",
43-
"prismarine-chunk": "^1.20.1",
44-
"prismarine-entity": "^2.0.0",
45-
"prismarine-item": "^1.5.0",
46-
"prismarine-nbt": "^2.0.0",
47-
"prismarine-provider-anvil": "^2.3.0",
48-
"prismarine-windows": "^2.0.0",
49-
"prismarine-world": "^3.0.0",
45+
"prismarine-chunk": "^1.34.0",
46+
"prismarine-entity": "^2.2.0",
47+
"prismarine-item": "^1.14.0",
48+
"prismarine-nbt": "^2.2.1",
49+
"prismarine-provider-anvil": "^2.7.0",
50+
"prismarine-windows": "^2.8.0",
51+
"prismarine-world": "^3.6.2",
5052
"random-seed": "^0.3.0",
5153
"range": "^0.0.3",
5254
"readline": "^1.3.0",

src/globals.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// make process.platform also accept browser
2+
declare namespace NodeJS {
3+
interface Process {
4+
platform: string;
5+
browser?: boolean
6+
}
7+
8+
}
9+
interface NodeRequire {
10+
// webpack bundling
11+
context: (path: string, deep: boolean, filter: RegExp) => { keys: () => string[]; (id: string): any };
12+
}

src/index.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
if (typeof process !== 'undefined' && parseInt(process.versions.node.split('.')[0]) < 18) {
1+
if (typeof process !== 'undefined' && !process.browser && process.platform !== 'browser' && parseInt(process.versions.node.split('.')[0]) < 18) {
22
console.error('[\x1b[31mCRITICAL\x1b[0m] Node.JS 18 or newer is required')
33
console.error('[\x1b[31mCRITICAL\x1b[0m] You can download the new version from https://nodejs.org/')
44
console.error(`[\x1b[31mCRITICAL\x1b[0m] Your current Node.JS version is: ${process.versions.node}`)
55
process.exit(1)
66
}
77

8-
const mc = require('minecraft-protocol')
8+
const { createServer } = require('minecraft-protocol')
9+
910
const EventEmitter = require('events').EventEmitter
10-
const path = require('path')
11-
const requireIndex = require('./lib/requireindex')
1211
const supportedVersions = require('./lib/version').supportedVersions
1312
const Command = require('./lib/command')
13+
const plugins = require('./lib/plugins')
1414
require('emit-then').register()
1515
if (process.env.NODE_ENV === 'dev') {
1616
require('longjohn')
@@ -38,8 +38,10 @@ function createMCServer (options) {
3838

3939
class MCServer extends EventEmitter {
4040
constructor () {
41+
plugins.initPlugins()
4142
super()
4243
this._server = null
44+
this.pluginsReady = false
4345
}
4446

4547
connect (options) {
@@ -48,13 +50,18 @@ class MCServer extends EventEmitter {
4850
throw new Error(`Version ${version.minecraftVersion} is not supported.`)
4951
}
5052
this.supportFeature = feature => supportFeature(feature, version.majorVersion)
51-
52-
const plugins = requireIndex(path.join(__dirname, 'lib', 'plugins'))
5353
this.commands = new Command({})
54-
this._server = mc.createServer(options)
55-
Object.keys(plugins)
56-
.filter(pluginName => plugins[pluginName].server !== undefined)
57-
.forEach(pluginName => plugins[pluginName].server(this, options))
54+
this._server = createServer(options)
55+
56+
const promises = []
57+
for (const plugin of plugins.builtinPlugins) {
58+
promises.push(plugin.server?.(this, options))
59+
}
60+
Promise.all(promises).then(() => {
61+
this.emit('pluginsReady')
62+
this.pluginsReady = true
63+
})
64+
5865
if (options.logging === true) this.createLog()
5966
this._server.on('error', error => this.emit('error', error))
6067
this._server.on('listening', () => this.emit('listening', this._server.socketServer.address().port))

0 commit comments

Comments
 (0)