Skip to content

Full TypeScript rewrite. Best type defs - #651

Open
zardoy wants to merge 25 commits into
PrismarineJS:masterfrom
zardoy:ts-master
Open

Full TypeScript rewrite. Best type defs#651
zardoy wants to merge 25 commits into
PrismarineJS:masterfrom
zardoy:ts-master

Conversation

@zardoy

@zardoy zardoy commented Jan 17, 2024

Copy link
Copy Markdown
Contributor

closes #323

Still a lot of commits to pick, but decided to open early to run a few checks (like jsdoc description & remove docs, generator). awaiting feedback

I have published module for testing so you can see the final result if interested (including docs URL): http://npmjs.com/package/@zardoy/flying-squid

(cherry picked from commit 1acb152)

with a lot of manual fixes
@zardoy
zardoy marked this pull request as draft January 17, 2024 16:18
const count = opt.count !== undefined
? opt.count
: (type === 'all' || type === 'entity' ? serv.entities.length : 1)
: (type === 'all' || type === 'entity' ? Object.keys(serv.entities).length : 1)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛

Comment thread src/lib/modules/communication.ts
Comment thread src/lib/modules/communication.ts
Comment thread src/lib/modules/communication.ts Outdated
"_writeArray": (packetName: any, packetFields: any, players: any) => any
"_writeNearby": (packetName: any, packetFields: any, loc: any) => any
"getNearby": ({ world, position, radius }: { world: any; position: any; radius?: number | undefined }) => any
"getNearbyEntities": ({ world, position, radius }: { world: any; position: any; radius?: number | undefined }) => any[]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to remove destructure from types.

Comment thread src/lib/modules/effects.ts Outdated
entity.sendEffect = (effectId, { amplifier = 0, duration = 30 * 20, particles = true, whitelist, blacklist = [] } = {}) => {
if (!whitelist) whitelist = serv.getNearby(entity)
if (entity.type === 'player' && [1].indexOf(effectId) !== -1) entity.sendAbilities()
if (entity.type === 'player' && [1].indexOf(effectId) !== -1) (entity as Player).sendAbilities()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could turn entity into union of "common entity" and "player" so checks like entity.type === player would narrow the type to Player. But it's not possible since we re using interfaces for per-module extension and not types. So it would be easier to just mention this trick

Comment thread src/lib/modules/effects.ts
Comment thread src/lib/modules/login.ts
Comment thread src/lib/modules/moderation.ts Outdated
const block = await player.world.getBlock(referencePosition)
block.position = referencePosition
//@ts-ignore TODO
block.direction = direction

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this pattern is widely used across prismarine projects, imo it shouldn't as it makes debugging hard

Comment thread src/lib/modules/chat.ts Outdated
zardoy added 14 commits January 17, 2024 23:24
remove almost all require's, make mcData typed (expose), make types ready for publishing!

(cherry picked from commit e4be9d8)
add block renames

(cherry picked from commit b9a2813)
(cherry picked from commit f324dc4)
…rnal

remove api.md for now
fix gentypes

(cherry picked from commit dd3253e)
(cherry picked from commit ba37e8d)
(cherry picked from commit b81e264)
(cherry picked from commit 692f062)
@socket-security

socket-security Bot commented Jan 17, 2024

Copy link
Copy Markdown

No top level dependency changes detected. Learn more about Socket for GitHub ↗︎

@zardoy

zardoy commented Jan 17, 2024

Copy link
Copy Markdown
Contributor Author

almost done, I picked everything. will review it once again and mark it as ready.

I tried ts-standard instead of standard. While it uses some useful rules like @typescript-eslint/no-floating-promises it also includes too strict rules like @typescript-eslint/explicit-function-return-type that require you to write more code. Overall ts-standard ruleset looks good to me so if you want I can fix all errors.

@rom1504

rom1504 commented Jan 17, 2024

Copy link
Copy Markdown
Member

Please fix tests

@rom1504

rom1504 commented Jan 17, 2024

Copy link
Copy Markdown
Member

I see you deleted the doc. Can you show what a generated doc would look like ?

@rom1504

rom1504 commented Jan 17, 2024

Copy link
Copy Markdown
Member

Overall doesn't look worse than before.

But also it's not super clear to me what we gain here. Just adds a bunch of syntactic complexity and maybe a small amount of type safeness.

What do you think @extremeheat ?

@rom1504

rom1504 commented Jan 17, 2024

Copy link
Copy Markdown
Member

https://github.com/zardoy/space-squid/tree/ts-master/src/lib this is the part that may maybe benefit from TS but somehow you didn't convert it. Any reason?

@zardoy

zardoy commented Jan 17, 2024

Copy link
Copy Markdown
Contributor Author

I see you deleted the doc. Can you show what a generated doc would look like ?

Yes give me a moment. Also let's discuss it in the last step, not the biggest issue here imo.

Just adds a bunch of syntactic complexity

Let's revert things you don't like here. I just want basic IntelliSense for serv/entity/player (and typings!).

small amount of type safeness.

Yes I fixed a small amount of bugs that types helped me to catch. But in the current state the codebase is almost fully typed so referencing would allow you to prototype faster. Or did you mean something else?

zardoy/space-squid@ts-master/src/lib

can convert them as well if needed, I think this is just 5% of the codebase. I was really focused on plugins, so if plugins code look good...

@zardoy zardoy left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also let's remove duplicating code const registry = require('prismarine-registry')(version) and instead set it to server like serv.resitry = ...

Comment thread src/lib/plugins/log.ts Outdated
} finally {
process.stdout = tmp
}
// let tmp

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to revert i guess

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was the purpose of that? I remember it was broking output in some way

@zardoy zardoy Jan 18, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, got the issue, it worked before because the code was not in strict mode (which is not good)

Comment thread src/lib/plugins/login.ts
function updateInventory () {
playerData.inventory.forEach((item) => {
const itemName = item.id.value.slice(10) // skip game brand prefix
const itemValue: string | number = item.id.value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better inventory decode

Comment thread src/lib/plugins/moderation.ts Outdated
if (result) {
serv.emit('banned', ctx.player ? ctx.player : { username: '[@]' }, username, reason)
if (ctx.player) ctx.player.chat(username + ' was banned')
serv.emit('banned', (ctx.player != null) ? ctx.player : { username: '[@]' }, username, reason)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its standard-ts changed al these checks

@zardoy

zardoy commented Jan 17, 2024

Copy link
Copy Markdown
Contributor Author

I generated docs:
typedoc (without plugins) https://zardoy.github.io/space-squid/functions/createMCServer.html
real time generated docs from npm: https://paka.dev/npm/@zardoy/flying-squid

yes, need to clean up a few things like position: any or getSpawnPacket. But look at props like bannedPlayers that can reference other code. Imo all this looks useful.
But I'm just curious which generator do you think looks cleaner?

EDIT: there is a lot of noise on paka because it also included internal API, gonna update in a second

Comment thread src/lib/plugins/world.ts
player.sendMap = () => {
return player.sendNearbyChunks(Math.min(3, settings['view-distance']))
.catch((err) => setTimeout(() => { throw err }), 0)
return player.sendNearbyChunks(settings['view-distance'])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logic was hardcoded, I want to send more chunks

Comment thread src/lib/plugins/tp.ts
parse (str) {
return str.match(/^(((.* )?~?-?\d* ~?-?\d* ~?-?\d*)|(.+ .+))$/) ? str.split(' ') : false
},
action (args, ctx) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewrite tp implementation (fixed it)

const matches = allMatches.filter((match) => match.startsWith(existingContent))
player._client.write('tab_complete', {
matches: matches.filter((match) => match.startsWith(existingContent))
matches: !registry.supportFeature('tabCompleteHasAToolTip')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix crash on completions send in newer versions

Comment thread src/lib/plugins/sound.ts
serv.playSound(sound, player.world, null, { ...opt, whitelist: player })
}

// player.on('placeBlock_cancel', async ({ reference }, cancel) => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented since events are not implemented in the code

}
player._client.on('settings', ({ viewDistance }) => {
player.view = viewDistance
player.sendRestMap()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bugfix, chunks with new view distance should be sended

Comment thread src/lib/plugins/pvp.ts
arr.forEach(entity => {
entity.takeDamage({ damage: 20 })
serv.info(`Killed ${colors.bold(entity.type === 'player' ? entity.username : entity.name)}`)
serv.info(`Killed ${colors.bold(entity.type === 'player' ? (entity as Player).username : entity.name ?? '<unknown>')}`)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entity can be without name in some cases

Comment thread src/lib/plugins/pvp.ts
function attackEntity (entityId) {
const attackedEntity = serv.entities[entityId]
if (!attackedEntity || (attackedEntity.gameMode !== 0 && attackedEntity.type === 'player')) return
const attackedPlayer = attackedEntity.type === 'player' ? attackedEntity as Player : undefined

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes a bit more verbose, but doesn't it look cleaner?

op: true,
parse (args, ctx) {
args = args.split(' ')
parse (_args, ctx) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ts pattern.

Comment thread src/lib/plugins/log.ts
player.on('disconnected', () => serv.info(player.username + ' disconnected'))
player.on('chat', ({ message }) => serv.info('<' + player.username + '>' + ' ' + message))
player.on('disconnected', (reason) => serv.info(player.username + ' disconnected. Reason: ' + reason))
// player.on('chat', ({ message }) => serv.info('<' + player.username + '>' + ' ' + message))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chat event not implemented yet

Comment thread src/lib/plugins/index.ts
// esbuild custom plugin
const files = require(/* webpackIgnore: true */ 'esbuild-import-glob(path:.,skipFiles:index.js,external.js)')
module.exports.builtinPlugins = Object.values(files)
const files = require(/* webpackIgnore: true */ 'esbuild-import-glob(path:.,skipFiles:index.js,external.js,index.ts,external.ts)')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we now have build step I think it would be better to remove these workarounds and instead create index.js with all plugins included. So we don't need to handle different platforms

@@ -180,7 +181,7 @@ module.exports.player = function (player, serv, { version }) {
player.changeBlock(data.position, 0, 0)
const aboveBlock = await player.world.getBlock(data.position.offset(0, 1, 0))
if (aboveBlock.material === 'plant') {
await player.setBlock(data.position.offset(0, 1, 0), 0, 0)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setblock doesn't have 3rd arg, could lead to potential bug

})

if (type === 'near') sample.sort((a, b) => a.position.distanceTo(opt.pos) > b.position.distanceTo(opt.pos))
if (type === 'near') sample.sort((a, b) => a.position.distanceTo(opt.pos) - b.position.distanceTo(opt.pos))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this sorting was incorrect 😄 at least that one was caught by ts

})

sample = sample.filter(s => {
changeType<Player & Entity & { team, scores }>(s) // TODO implement team & scores

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an easy way to change any type without writing a bunch of unnecessary TS stuff

Comment thread src/lib/plugins/blocks.ts
const data = parseInt(params[5] || 0, 10)
const stateId = registry.supportFeature('theFlattening') ? (blocks[id].minStateId + data) : (id << 4 | data)
const data = parseInt(params[5] || '0', 10)
const stateId = postFlatenning

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setblock was setting incorrect default state, fixed

Comment thread src/lib/plugins/blocks.ts
serv.players
.filter(p => p.world === player.world && player !== p)
.forEach(p => p.sendBlock(position, blockType, blockData))
.forEach(p => p.sendBlock(position, blockType/* , blockData */)) // todo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was handled incorrectly anyway

Comment thread src/lib/command.ts
const UserError = require('./user_error')
import UserError from './user_error'

type Ctx<P extends boolean> = P extends true ? {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i tried to not add a lot of generics, but this one here is just super useful for other plugins

@zardoy zardoy left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found a few unrelated to js->ts migration, they are not big, but i can remove them if they are blocking this pr

@zardoy

zardoy commented Jan 18, 2024

Copy link
Copy Markdown
Contributor Author

yes, I agree, typedoc is not the best generator here and its default look is really bad, but at least everything is structured. Anyway, I'm happy with making it all work, now its up to you whether to use it or not. If you still don't want to have anything like this, we can simply removing old docs (but I wouldn't be happy to keeping them up to date)

@zardoy

zardoy commented Jan 18, 2024

Copy link
Copy Markdown
Contributor Author

the current build time is not ok, if everything else is ok, I will add esbuild-based pipeline for faster dev builds.

also I'm curious why !src/plugins/README.md is in .npmignore? whats the point of publishing it?

@zardoy
zardoy marked this pull request as ready for review January 18, 2024 16:23
@zardoy zardoy changed the title TS 2 Full TypeScript rewrite. Best type defs Apr 18, 2024
@Pandapip1

Copy link
Copy Markdown
Contributor

+1 to using typescript. Having even a small amount of type safety saves so much debugging time.

@zardoy

zardoy commented Apr 22, 2024

Copy link
Copy Markdown
Contributor Author

saves so much debugging time.

You are right. If you write new modules or plugins you can catch bugs with the right types earlier.
But I'm doing it mostly for better DX and a way to pick definitions faster. This all just makes a huge difference for us and unfortunately, we simply can't work without types there. Also, unlike in mineflayer I can guarantee these types are correct since the source code reuses them. Btw can look at http://npmjs.com/package/@zardoy/flying-squid for a real demo

@rom1504

rom1504 commented Jun 9, 2024

Copy link
Copy Markdown
Member

Tests still failing, my comments on libs/ not being converted is still valid.

Should we close this ?

@zardoy

zardoy commented Jun 9, 2024

Copy link
Copy Markdown
Contributor Author

Tests still failing, my comments on libs/ not being converted is still valid.

I can fix tests if everything else is ok. Can you link the comments you are referring to?

Should we close this ?

Closing this one, means discarding a huge contribution, why don't you want to go in sync with my repo?

@rom1504

rom1504 commented Jun 9, 2024

Copy link
Copy Markdown
Member

#651 (comment)

@zardoy

zardoy commented Jun 10, 2024

Copy link
Copy Markdown
Contributor Author

#651 (comment)

Oh, ok, will do that, I will have time at the end of the week for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Waiting for user input

Development

Successfully merging this pull request may close these issues.

Flow/TypeScript

3 participants