@v12-engine/engine 0.1.0 → 0.4.0
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.
- package/dist/abilities/amounts.d.ts +28 -0
- package/dist/abilities/amounts.js +74 -0
- package/dist/abilities/amounts.js.map +1 -0
- package/dist/abilities/buffs.d.ts +106 -6
- package/dist/abilities/buffs.js +443 -0
- package/dist/abilities/buffs.js.map +1 -0
- package/dist/abilities/castTold.d.ts +2 -0
- package/dist/abilities/castTold.js +66 -0
- package/dist/abilities/castTold.js.map +1 -0
- package/dist/abilities/chance.d.ts +38 -0
- package/dist/abilities/chance.js +77 -0
- package/dist/abilities/chance.js.map +1 -0
- package/dist/abilities/charges.d.ts +18 -0
- package/dist/abilities/charges.js +101 -0
- package/dist/abilities/charges.js.map +1 -0
- package/dist/abilities/control.d.ts +28 -0
- package/dist/abilities/control.js +113 -0
- package/dist/abilities/control.js.map +1 -0
- package/dist/abilities/dispel.js +51 -0
- package/dist/abilities/dispel.js.map +1 -0
- package/dist/abilities/effectFields.d.ts +1 -1
- package/dist/abilities/effectFields.js +676 -0
- package/dist/abilities/effectFields.js.map +1 -0
- package/dist/abilities/effectIndex.d.ts +27 -0
- package/dist/abilities/effectIndex.js +64 -0
- package/dist/abilities/effectIndex.js.map +1 -0
- package/dist/abilities/effects/buff.d.ts +9 -3
- package/dist/abilities/effects/buff.js +84 -0
- package/dist/abilities/effects/buff.js.map +1 -0
- package/dist/abilities/effects/conditions.d.ts +2 -0
- package/dist/abilities/effects/conditions.js +41 -0
- package/dist/abilities/effects/conditions.js.map +1 -0
- package/dist/abilities/effects/core.js +54 -0
- package/dist/abilities/effects/core.js.map +1 -0
- package/dist/abilities/effects/damage.d.ts +1 -1
- package/dist/abilities/effects/damage.js +43 -0
- package/dist/abilities/effects/damage.js.map +1 -0
- package/dist/abilities/effects/heal.d.ts +2 -2
- package/dist/abilities/effects/heal.js +51 -0
- package/dist/abilities/effects/heal.js.map +1 -0
- package/dist/abilities/effects/missile.d.ts +13 -1
- package/dist/abilities/effects/missile.js +104 -0
- package/dist/abilities/effects/missile.js.map +1 -0
- package/dist/abilities/effects/morph.d.ts +5 -0
- package/dist/abilities/effects/morph.js +43 -0
- package/dist/abilities/effects/morph.js.map +1 -0
- package/dist/abilities/effects/moves.d.ts +16 -4
- package/dist/abilities/effects/moves.js +212 -0
- package/dist/abilities/effects/moves.js.map +1 -0
- package/dist/abilities/effects/passives.d.ts +19 -9
- package/dist/abilities/effects/passives.js +131 -0
- package/dist/abilities/effects/passives.js.map +1 -0
- package/dist/abilities/effects/proxy.d.ts +5 -0
- package/dist/abilities/effects/proxy.js +60 -0
- package/dist/abilities/effects/proxy.js.map +1 -0
- package/dist/abilities/effects/sight.d.ts +2 -2
- package/dist/abilities/effects/sight.js +39 -0
- package/dist/abilities/effects/sight.js.map +1 -0
- package/dist/abilities/effects/spawn.d.ts +5 -2
- package/dist/abilities/effects/spawn.js +40 -0
- package/dist/abilities/effects/spawn.js.map +1 -0
- package/dist/abilities/effects/timed.d.ts +2 -2
- package/dist/abilities/effects/timed.js +96 -0
- package/dist/abilities/effects/timed.js.map +1 -0
- package/dist/abilities/effects/trade.d.ts +1 -1
- package/dist/abilities/effects/trade.js +25 -0
- package/dist/abilities/effects/trade.js.map +1 -0
- package/dist/abilities/effects.d.ts +97 -23
- package/dist/abilities/effects.js +860 -0
- package/dist/abilities/effects.js.map +1 -0
- package/dist/abilities/land.d.ts +38 -4
- package/dist/abilities/land.js +189 -0
- package/dist/abilities/land.js.map +1 -0
- package/dist/abilities/recast.d.ts +45 -0
- package/dist/abilities/recast.js +118 -0
- package/dist/abilities/recast.js.map +1 -0
- package/dist/abilities/scaled.js +40 -0
- package/dist/abilities/scaled.js.map +1 -0
- package/dist/abilities/screen.d.ts +6 -0
- package/dist/abilities/screen.js +83 -0
- package/dist/abilities/screen.js.map +1 -0
- package/dist/abilities/shapes.d.ts +66 -0
- package/dist/abilities/shapes.js +122 -0
- package/dist/abilities/shapes.js.map +1 -0
- package/dist/abilities/text.d.ts +14 -2
- package/dist/abilities/text.js +204 -0
- package/dist/abilities/text.js.map +1 -0
- package/dist/abilities/triggers.d.ts +98 -0
- package/dist/abilities/triggers.js +351 -0
- package/dist/abilities/triggers.js.map +1 -0
- package/dist/abilityChanges.d.ts +131 -0
- package/dist/abilityChanges.js +345 -0
- package/dist/abilityChanges.js.map +1 -0
- package/dist/ai/area.d.ts +4 -1
- package/dist/ai/brain.d.ts +4 -1
- package/dist/ai/brain.js +844 -0
- package/dist/ai/brain.js.map +1 -0
- package/dist/ai/budget.js +74 -0
- package/dist/ai/budget.js.map +1 -0
- package/dist/ai/checks.d.ts +9 -1
- package/dist/ai/checks.js +242 -0
- package/dist/ai/checks.js.map +1 -0
- package/dist/ai/computer.d.ts +11 -4
- package/dist/ai/computer.js +251 -0
- package/dist/ai/computer.js.map +1 -0
- package/dist/ai/defence.d.ts +4 -0
- package/dist/ai/defence.js +430 -0
- package/dist/ai/defence.js.map +1 -0
- package/dist/ai/define.d.ts +3 -2
- package/dist/ai/define.js +31 -0
- package/dist/ai/define.js.map +1 -0
- package/dist/ai/economy.d.ts +10 -7
- package/dist/ai/economy.js +780 -0
- package/dist/ai/economy.js.map +1 -0
- package/dist/ai/geometry.js +38 -0
- package/dist/ai/geometry.js.map +1 -0
- package/dist/ai/goals.d.ts +1 -0
- package/dist/ai/goals.js +560 -0
- package/dist/ai/goals.js.map +1 -0
- package/dist/ai/knowledge.d.ts +72 -7
- package/dist/ai/knowledge.js +804 -0
- package/dist/ai/knowledge.js.map +1 -0
- package/dist/ai/lanes.d.ts +10 -2
- package/dist/ai/lanes.js +715 -0
- package/dist/ai/lanes.js.map +1 -0
- package/dist/ai/melee/build.js +264 -0
- package/dist/ai/melee/build.js.map +1 -0
- package/dist/ai/melee/goals.d.ts +7 -0
- package/dist/ai/melee/goals.js +720 -0
- package/dist/ai/melee/goals.js.map +1 -0
- package/dist/ai/melee/heroes.js +68 -0
- package/dist/ai/melee/heroes.js.map +1 -0
- package/dist/ai/melee/index.d.ts +1 -1
- package/dist/ai/melee/index.js +51 -0
- package/dist/ai/melee/index.js.map +1 -0
- package/dist/ai/melee/production.js +29 -0
- package/dist/ai/melee/production.js.map +1 -0
- package/dist/ai/melee/surrender.js +64 -0
- package/dist/ai/melee/surrender.js.map +1 -0
- package/dist/ai/micro.d.ts +9 -0
- package/dist/ai/micro.js +1316 -0
- package/dist/ai/micro.js.map +1 -0
- package/dist/ai/mission.d.ts +9 -2
- package/dist/ai/mission.js +538 -0
- package/dist/ai/mission.js.map +1 -0
- package/dist/ai/missionRules.d.ts +1 -1
- package/dist/ai/missionRules.js +26 -0
- package/dist/ai/missionRules.js.map +1 -0
- package/dist/ai/module.d.ts +6 -2
- package/dist/ai/module.js +37 -0
- package/dist/ai/module.js.map +1 -0
- package/dist/ai/options.d.ts +3 -3
- package/dist/ai/plans/build.d.ts +20 -1
- package/dist/ai/plans/build.js +608 -0
- package/dist/ai/plans/build.js.map +1 -0
- package/dist/ai/plans/placement.d.ts +4 -1
- package/dist/ai/plans/placement.js +295 -0
- package/dist/ai/plans/placement.js.map +1 -0
- package/dist/ai/plans/plans.d.ts +9 -0
- package/dist/ai/plans/plans.js +110 -0
- package/dist/ai/plans/plans.js.map +1 -0
- package/dist/ai/plans/production.d.ts +14 -2
- package/dist/ai/plans/production.js +434 -0
- package/dist/ai/plans/production.js.map +1 -0
- package/dist/ai/plans/purse.d.ts +6 -3
- package/dist/ai/plans/purse.js +139 -0
- package/dist/ai/plans/purse.js.map +1 -0
- package/dist/ai/plans/tech.d.ts +6 -0
- package/dist/ai/plans/tech.js +122 -0
- package/dist/ai/plans/tech.js.map +1 -0
- package/dist/ai/profiles.d.ts +4 -4
- package/dist/{profiles-B3NS9jwk.js → ai/profiles.js} +3 -98
- package/dist/ai/profiles.js.map +1 -0
- package/dist/ai/reach.js +54 -0
- package/dist/ai/reach.js.map +1 -0
- package/dist/ai/report.d.ts +8 -0
- package/dist/ai/report.js +87 -0
- package/dist/ai/report.js.map +1 -0
- package/dist/ai/roles.d.ts +4 -0
- package/dist/ai/roles.js +207 -0
- package/dist/ai/roles.js.map +1 -0
- package/dist/ai/rules.d.ts +8 -2
- package/dist/ai/rules.js +416 -0
- package/dist/ai/rules.js.map +1 -0
- package/dist/ai/scouting.d.ts +6 -0
- package/dist/ai/scouting.js +324 -0
- package/dist/ai/scouting.js.map +1 -0
- package/dist/ai/settings.js +99 -0
- package/dist/ai/settings.js.map +1 -0
- package/dist/ai/squads.d.ts +21 -2
- package/dist/ai/squads.js +916 -0
- package/dist/ai/squads.js.map +1 -0
- package/dist/ai/strength.js +320 -0
- package/dist/ai/strength.js.map +1 -0
- package/dist/ai/task.d.ts +3 -1
- package/dist/ai/task.js +17 -0
- package/dist/ai/task.js.map +1 -0
- package/dist/ai/tasks/attack.js +24 -0
- package/dist/ai/tasks/attack.js.map +1 -0
- package/dist/ai/tasks/capture.d.ts +3 -3
- package/dist/ai/tasks/capture.js +37 -0
- package/dist/ai/tasks/capture.js.map +1 -0
- package/dist/ai/tasks/creep.js +29 -0
- package/dist/ai/tasks/creep.js.map +1 -0
- package/dist/ai/tasks/defend.js +61 -0
- package/dist/ai/tasks/defend.js.map +1 -0
- package/dist/ai/tasks/drop.js +81 -0
- package/dist/ai/tasks/drop.js.map +1 -0
- package/dist/ai/tasks/escort.js +59 -0
- package/dist/ai/tasks/escort.js.map +1 -0
- package/dist/ai/tasks/gather.js +48 -0
- package/dist/ai/tasks/gather.js.map +1 -0
- package/dist/ai/tasks/harass.js +39 -0
- package/dist/ai/tasks/harass.js.map +1 -0
- package/dist/ai/tasks/hold.js +32 -0
- package/dist/ai/tasks/hold.js.map +1 -0
- package/dist/ai/tasks/index.js +34 -0
- package/dist/ai/tasks/index.js.map +1 -0
- package/dist/ai/tasks/move.js +26 -0
- package/dist/ai/tasks/move.js.map +1 -0
- package/dist/ai/tasks/patrol.js +59 -0
- package/dist/ai/tasks/patrol.js.map +1 -0
- package/dist/ai/tasks/retreat.js +28 -0
- package/dist/ai/tasks/retreat.js.map +1 -0
- package/dist/ai/tasks/scout.js +73 -0
- package/dist/ai/tasks/scout.js.map +1 -0
- package/dist/ai/tasks/shared.d.ts +1 -1
- package/dist/ai/tasks/shared.js +137 -0
- package/dist/ai/tasks/shared.js.map +1 -0
- package/dist/ai/team.d.ts +18 -1
- package/dist/ai/team.js +492 -0
- package/dist/ai/team.js.map +1 -0
- package/dist/ai/threat.d.ts +6 -0
- package/dist/ai/threat.js +206 -0
- package/dist/ai/threat.js.map +1 -0
- package/dist/ai/view.d.ts +21 -7
- package/dist/ai/view.js +677 -0
- package/dist/ai/view.js.map +1 -0
- package/dist/ai/waves.d.ts +6 -0
- package/dist/ai/waves.js +410 -0
- package/dist/ai/waves.js.map +1 -0
- package/dist/ai.js +54 -6239
- package/dist/aiTesting.d.ts +6 -3
- package/dist/aiTesting.js +229 -0
- package/dist/aiTesting.js.map +1 -0
- package/dist/areas.d.ts +4 -1
- package/dist/areas.js +295 -0
- package/dist/areas.js.map +1 -0
- package/dist/attacks.d.ts +3 -3
- package/dist/attacks.js +127 -0
- package/dist/attacks.js.map +1 -0
- package/dist/burning.d.ts +267 -0
- package/dist/burning.js +795 -0
- package/dist/burning.js.map +1 -0
- package/dist/bytes.js +21 -0
- package/dist/bytes.js.map +1 -0
- package/dist/callings.d.ts +71 -0
- package/dist/callings.js +157 -0
- package/dist/callings.js.map +1 -0
- package/dist/cameraOrders.d.ts +78 -0
- package/dist/cameraOrders.js +114 -0
- package/dist/cameraOrders.js.map +1 -0
- package/dist/camps.d.ts +70 -4
- package/dist/camps.js +501 -0
- package/dist/camps.js.map +1 -0
- package/dist/capture.d.ts +15 -3
- package/dist/capture.js +304 -0
- package/dist/capture.js.map +1 -0
- package/dist/cellLayers.d.ts +78 -13
- package/dist/cellLayers.js +275 -0
- package/dist/cellLayers.js.map +1 -0
- package/dist/cellPatches.d.ts +24 -0
- package/dist/cellPatches.js +73 -0
- package/dist/cellPatches.js.map +1 -0
- package/dist/cinematics.d.ts +10 -5
- package/dist/cinematics.js +161 -0
- package/dist/cinematics.js.map +1 -0
- package/dist/clock.d.ts +2 -2
- package/dist/clock.js +78 -0
- package/dist/clock.js.map +1 -0
- package/dist/commands.d.ts +36 -4
- package/dist/commands.js +636 -0
- package/dist/commands.js.map +1 -0
- package/dist/components.d.ts +31 -10
- package/dist/components.js +74 -0
- package/dist/components.js.map +1 -0
- package/dist/computers.d.ts +4 -4
- package/dist/computers.js +194 -0
- package/dist/computers.js.map +1 -0
- package/dist/concealment.d.ts +25 -2
- package/dist/concealment.js +409 -0
- package/dist/concealment.js.map +1 -0
- package/dist/coreComponents.js +130 -0
- package/dist/coreComponents.js.map +1 -0
- package/dist/createGame.d.ts +306 -0
- package/dist/createGame.js +405 -0
- package/dist/createGame.js.map +1 -0
- package/dist/dashing.d.ts +18 -2
- package/dist/dashing.js +41 -0
- package/dist/dashing.js.map +1 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +5 -3
- package/dist/debug2.js +76 -0
- package/dist/debug2.js.map +1 -0
- package/dist/debugCommands.d.ts +6 -6
- package/dist/debugCommands.js +537 -0
- package/dist/debugCommands.js.map +1 -0
- package/dist/debugging.d.ts +1 -0
- package/dist/defaultSets.d.ts +183 -0
- package/dist/defaultSets.js +216 -0
- package/dist/defaultSets.js.map +1 -0
- package/dist/definitions.d.ts +6 -0
- package/dist/definitions.js +78 -0
- package/dist/definitions.js.map +1 -0
- package/dist/defs.d.ts +1895 -165
- package/dist/defs.js +380 -0
- package/dist/defs.js.map +1 -0
- package/dist/denying.d.ts +4 -4
- package/dist/denying.js +106 -0
- package/dist/denying.js.map +1 -0
- package/dist/destructibles.d.ts +2 -2
- package/dist/destructibles.js +36 -0
- package/dist/destructibles.js.map +1 -0
- package/dist/determinism.d.ts +4 -4
- package/dist/determinism.js +247 -0
- package/dist/determinism.js.map +1 -0
- package/dist/dialog.d.ts +17 -2
- package/dist/dialog.js +108 -0
- package/dist/dialog.js.map +1 -0
- package/dist/effectRefs.js +28 -0
- package/dist/effectRefs.js.map +1 -0
- package/dist/effects.d.ts +27 -11
- package/dist/effects.js +110 -0
- package/dist/effects.js.map +1 -0
- package/dist/endings.d.ts +1 -1
- package/dist/endings.js +78 -0
- package/dist/endings.js.map +1 -0
- package/dist/engagement.d.ts +40 -6
- package/dist/engagement.js +530 -0
- package/dist/engagement.js.map +1 -0
- package/dist/engineEvents.d.ts +72 -9
- package/dist/engineEvents.js +520 -0
- package/dist/engineEvents.js.map +1 -0
- package/dist/entity.d.ts +275 -26
- package/dist/entity.js +441 -0
- package/dist/entity.js.map +1 -0
- package/dist/entityIndex.d.ts +10 -3
- package/dist/entityIndex.js +149 -0
- package/dist/entityIndex.js.map +1 -0
- package/dist/errors.d.ts +6 -2
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +11 -5
- package/dist/events.js +158 -0
- package/dist/events.js.map +1 -0
- package/dist/exactMath.js +273 -0
- package/dist/exactMath.js.map +1 -0
- package/dist/features.d.ts +1 -1
- package/dist/features.js +127 -0
- package/dist/features.js.map +1 -0
- package/dist/fields.d.ts +25 -5
- package/dist/fields.js +140 -0
- package/dist/fields.js.map +1 -0
- package/dist/fixture.d.ts +3 -3
- package/dist/fixture.js +512 -0
- package/dist/fixture.js.map +1 -0
- package/dist/fog.d.ts +26 -3
- package/dist/fog.js +333 -0
- package/dist/fog.js.map +1 -0
- package/dist/foliage.d.ts +476 -0
- package/dist/foliage.js +1478 -0
- package/dist/foliage.js.map +1 -0
- package/dist/foliageDebug.d.ts +57 -0
- package/dist/foliageDebug.js +172 -0
- package/dist/foliageDebug.js.map +1 -0
- package/dist/foliageRegrowth.d.ts +20 -0
- package/dist/foliageRegrowth.js +332 -0
- package/dist/foliageRegrowth.js.map +1 -0
- package/dist/formState.d.ts +67 -0
- package/dist/formState.js +65 -0
- package/dist/formState.js.map +1 -0
- package/dist/formation.d.ts +46 -8
- package/dist/formation.js +297 -0
- package/dist/formation.js.map +1 -0
- package/dist/forms.d.ts +58 -0
- package/dist/forms.js +146 -0
- package/dist/forms.js.map +1 -0
- package/dist/friendlyFire.d.ts +2 -2
- package/dist/friendlyFire.js +37 -0
- package/dist/friendlyFire.js.map +1 -0
- package/dist/game.d.ts +141 -48
- package/dist/game.js +834 -0
- package/dist/game.js.map +1 -0
- package/dist/gameData.d.ts +96 -22
- package/dist/gameData.js +1690 -0
- package/dist/gameData.js.map +1 -0
- package/dist/gameFiles.d.ts +3 -3
- package/dist/gameFiles.js +14 -0
- package/dist/gameFiles.js.map +1 -0
- package/dist/gates.js +92 -0
- package/dist/gates.js.map +1 -0
- package/dist/generate.d.ts +28 -1
- package/dist/generate.js +1062 -0
- package/dist/generate.js.map +1 -0
- package/dist/generateWorker.d.ts +7 -3
- package/dist/generateWorker.js +86 -0
- package/dist/generateWorker.js.map +1 -0
- package/dist/grid.d.ts +9 -0
- package/dist/grid.js +102 -0
- package/dist/grid.js.map +1 -0
- package/dist/ground.d.ts +17 -1
- package/dist/ground.js +375 -0
- package/dist/ground.js.map +1 -0
- package/dist/groundObjects.d.ts +70 -0
- package/dist/groundObjects.js +371 -0
- package/dist/groundObjects.js.map +1 -0
- package/dist/groundSpeed.d.ts +20 -3
- package/dist/groundSpeed.js +117 -0
- package/dist/groundSpeed.js.map +1 -0
- package/dist/hero.d.ts +126 -10
- package/dist/hero.js +432 -0
- package/dist/hero.js.map +1 -0
- package/dist/holds.d.ts +94 -0
- package/dist/holds.js +305 -0
- package/dist/holds.js.map +1 -0
- package/dist/home.d.ts +1 -1
- package/dist/home.js +30 -0
- package/dist/home.js.map +1 -0
- package/dist/hooks.d.ts +339 -57
- package/dist/hooks.js +105 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hotReload.d.ts +6 -5
- package/dist/hotReload.js +145 -0
- package/dist/hotReload.js.map +1 -0
- package/dist/ids.d.ts +3 -3
- package/dist/ids.js +30 -0
- package/dist/ids.js.map +1 -0
- package/dist/idsFile.d.ts +36 -0
- package/dist/idsFile.js +134 -0
- package/dist/idsFile.js.map +1 -0
- package/dist/index.d.ts +105 -68
- package/dist/index.js +194 -4701
- package/dist/inspect.d.ts +29 -2
- package/dist/{inspect-D9dWHmZ0.js → inspect.js} +21 -12
- package/dist/inspect.js.map +1 -0
- package/dist/inventory.d.ts +14 -1
- package/dist/inventory.js +84 -0
- package/dist/inventory.js.map +1 -0
- package/dist/items.d.ts +136 -8
- package/dist/items.js +585 -0
- package/dist/items.js.map +1 -0
- package/dist/lanes.d.ts +12 -3
- package/dist/lanes.js +496 -0
- package/dist/lanes.js.map +1 -0
- package/dist/life.d.ts +6 -2
- package/dist/life.js +287 -0
- package/dist/life.js.map +1 -0
- package/dist/log.d.ts +26 -3
- package/dist/log.js +192 -0
- package/dist/log.js.map +1 -0
- package/dist/manaBurn.d.ts +2 -2
- package/dist/manaBurn.js +75 -0
- package/dist/manaBurn.js.map +1 -0
- package/dist/map/file.d.ts +56 -3
- package/dist/map/file.js +264 -0
- package/dist/map/file.js.map +1 -0
- package/dist/map/images.js +61 -0
- package/dist/map/images.js.map +1 -0
- package/dist/map/look.d.ts +1 -1
- package/dist/map/look.js +30 -0
- package/dist/map/look.js.map +1 -0
- package/dist/map/pathProfile.js +183 -0
- package/dist/map/pathProfile.js.map +1 -0
- package/dist/map/resize.d.ts +4 -0
- package/dist/map/resize.js +136 -0
- package/dist/map/resize.js.map +1 -0
- package/dist/map/schema.js +257 -0
- package/dist/map/schema.js.map +1 -0
- package/dist/map/terrain.js +214 -0
- package/dist/map/terrain.js.map +1 -0
- package/dist/map/types.d.ts +92 -13
- package/dist/map/types.js +251 -0
- package/dist/map/types.js.map +1 -0
- package/dist/mapCheck/cells.js +94 -0
- package/dist/mapCheck/cells.js.map +1 -0
- package/dist/mapCheck/rules/foliage.d.ts +9 -0
- package/dist/mapCheck/rules/foliage.js +83 -0
- package/dist/mapCheck/rules/foliage.js.map +1 -0
- package/dist/mapCheck/rules/names.js +39 -0
- package/dist/mapCheck/rules/names.js.map +1 -0
- package/dist/mapCheck/rules/objects.d.ts +1 -1
- package/dist/mapCheck/rules/objects.js +56 -0
- package/dist/mapCheck/rules/objects.js.map +1 -0
- package/dist/mapCheck/rules/paths.js +32 -0
- package/dist/mapCheck/rules/paths.js.map +1 -0
- package/dist/mapCheck/rules/players.js +20 -0
- package/dist/mapCheck/rules/players.js.map +1 -0
- package/dist/mapCheck/rules/races.js +56 -0
- package/dist/mapCheck/rules/races.js.map +1 -0
- package/dist/mapCheck/rules/random.js +47 -0
- package/dist/mapCheck/rules/random.js.map +1 -0
- package/dist/mapCheck/rules/regions.js +30 -0
- package/dist/mapCheck/rules/regions.js.map +1 -0
- package/dist/mapCheck/rules/targets.js +31 -0
- package/dist/mapCheck/rules/targets.js.map +1 -0
- package/dist/mapCheck/rules/walking.js +45 -0
- package/dist/mapCheck/rules/walking.js.map +1 -0
- package/dist/mapCheck.d.ts +2 -2
- package/dist/mapCheck.js +63 -0
- package/dist/mapCheck.js.map +1 -0
- package/dist/mapFields.d.ts +25 -2
- package/dist/mapFields.js +134 -0
- package/dist/mapFields.js.map +1 -0
- package/dist/mapFile.js +176 -0
- package/dist/mapFile.js.map +1 -0
- package/dist/mapMeasure.d.ts +8 -0
- package/dist/mapMeasure.js +283 -0
- package/dist/mapMeasure.js.map +1 -0
- package/dist/mapMerge.d.ts +1 -0
- package/dist/mapMerge.js +172 -0
- package/dist/mapMerge.js.map +1 -0
- package/dist/mapObjectives.js +138 -0
- package/dist/mapObjectives.js.map +1 -0
- package/dist/mapObjects.js +80 -0
- package/dist/mapObjects.js.map +1 -0
- package/dist/math.d.ts +37 -7
- package/dist/math.js +155 -0
- package/dist/math.js.map +1 -0
- package/dist/messages.d.ts +71 -4
- package/dist/messages.js +29 -0
- package/dist/messages.js.map +1 -0
- package/dist/missionRules.d.ts +10 -2
- package/dist/{missionRules-C-bGxm0Q.js → missionRules.js} +30 -150
- package/dist/missionRules.js.map +1 -0
- package/dist/mods.d.ts +39 -20
- package/dist/mods.js +271 -0
- package/dist/mods.js.map +1 -0
- package/dist/moves.d.ts +87 -0
- package/dist/moves.js +539 -0
- package/dist/moves.js.map +1 -0
- package/dist/names.d.ts +7 -0
- package/dist/names.js +122 -0
- package/dist/names.js.map +1 -0
- package/dist/navigation/agentParams.js +14 -0
- package/dist/navigation/agentParams.js.map +1 -0
- package/dist/navigation/air.d.ts +7 -3
- package/dist/navigation/air.js +401 -0
- package/dist/navigation/air.js.map +1 -0
- package/dist/navigation/builds.d.ts +34 -2
- package/dist/navigation/builds.js +287 -0
- package/dist/navigation/builds.js.map +1 -0
- package/dist/navigation/groundAreas.d.ts +59 -1
- package/dist/navigation/groundAreas.js +407 -0
- package/dist/navigation/groundAreas.js.map +1 -0
- package/dist/navigation/navigation.d.ts +98 -9
- package/dist/navigation/recast.d.ts +194 -18
- package/dist/navigation/recast.js +1379 -0
- package/dist/navigation/recast.js.map +1 -0
- package/dist/navigation/sidePaths.d.ts +8 -3
- package/dist/navigation/sidePaths.js +241 -0
- package/dist/navigation/sidePaths.js.map +1 -0
- package/dist/navigation/sized.js +243 -0
- package/dist/navigation/sized.js.map +1 -0
- package/dist/nearest.js +32 -0
- package/dist/nearest.js.map +1 -0
- package/dist/noise.js +59 -0
- package/dist/noise.js.map +1 -0
- package/dist/objectives.d.ts +12 -3
- package/dist/objectives.js +144 -0
- package/dist/objectives.js.map +1 -0
- package/dist/orders/basic.d.ts +5 -5
- package/dist/orders/basic.js +417 -0
- package/dist/orders/basic.js.map +1 -0
- package/dist/orders/build.js +278 -0
- package/dist/orders/build.js.map +1 -0
- package/dist/orders/cast.d.ts +13 -3
- package/dist/orders/cast.js +558 -0
- package/dist/orders/cast.js.map +1 -0
- package/dist/orders/facts.js +16 -0
- package/dist/orders/facts.js.map +1 -0
- package/dist/orders/harvest.d.ts +21 -5
- package/dist/orders/harvest.js +540 -0
- package/dist/orders/harvest.js.map +1 -0
- package/dist/orders/idle.d.ts +3 -1
- package/dist/orders/idle.js +234 -0
- package/dist/orders/idle.js.map +1 -0
- package/dist/orders/items.js +92 -0
- package/dist/orders/items.js.map +1 -0
- package/dist/orders/queue.js +108 -0
- package/dist/orders/queue.js.map +1 -0
- package/dist/orders/waiting.d.ts +39 -0
- package/dist/orders/waiting.js +149 -0
- package/dist/orders/waiting.js.map +1 -0
- package/dist/pack.schema.js +13 -0
- package/dist/pack.schema.js.map +1 -0
- package/dist/pack.schema.json +7622 -841
- package/dist/packs/director/index.d.ts +13 -5
- package/dist/packs/director/index.js +204 -0
- package/dist/packs/director/index.js.map +1 -0
- package/dist/packs/heroes/campaign.d.ts +3 -2
- package/dist/packs/heroes/campaign.js +241 -0
- package/dist/packs/heroes/campaign.js.map +1 -0
- package/dist/packs/heroes/carry.d.ts +6 -0
- package/dist/packs/heroes/carry.js +112 -0
- package/dist/packs/heroes/carry.js.map +1 -0
- package/dist/packs/heroes/commands.js +115 -0
- package/dist/packs/heroes/commands.js.map +1 -0
- package/dist/packs/heroes/fallen.d.ts +43 -0
- package/dist/packs/heroes/fallen.js +61 -0
- package/dist/packs/heroes/fallen.js.map +1 -0
- package/dist/packs/heroes/hire.js +60 -0
- package/dist/packs/heroes/hire.js.map +1 -0
- package/dist/packs/heroes/index.d.ts +7 -6
- package/dist/packs/heroes/index.js +130 -0
- package/dist/packs/heroes/index.js.map +1 -0
- package/dist/packs/heroes/loop.d.ts +95 -0
- package/dist/packs/heroes/loop.js +250 -0
- package/dist/packs/heroes/loop.js.map +1 -0
- package/dist/packs/heroes/making.d.ts +6 -3
- package/dist/packs/heroes/making.js +55 -0
- package/dist/packs/heroes/making.js.map +1 -0
- package/dist/packs/heroes/respawn.d.ts +3 -3
- package/dist/packs/heroes/respawn.js +87 -0
- package/dist/packs/heroes/respawn.js.map +1 -0
- package/dist/packs/heroes/sets.d.ts +32 -0
- package/dist/packs/heroes/sets.js +51 -0
- package/dist/packs/heroes/sets.js.map +1 -0
- package/dist/packs/heroes/spells.d.ts +16 -0
- package/dist/packs/heroes/spells.js +269 -0
- package/dist/packs/heroes/spells.js.map +1 -0
- package/dist/packs/items/defaults.d.ts +43 -0
- package/dist/packs/items/defaults.js +1504 -0
- package/dist/packs/items/defaults.js.map +1 -0
- package/dist/packs/items/index.d.ts +27 -0
- package/dist/packs/items/index.js +214 -0
- package/dist/packs/items/index.js.map +1 -0
- package/dist/packs/moba/index.d.ts +3 -3
- package/dist/packs/moba/index.js +174 -0
- package/dist/packs/moba/index.js.map +1 -0
- package/dist/packs/pack.d.ts +15 -15
- package/dist/packs/pack.js +14 -0
- package/dist/packs/pack.js.map +1 -0
- package/dist/packs.d.ts +15 -7
- package/dist/packs.js +222 -0
- package/dist/packs.js.map +1 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +109 -0
- package/dist/paths.js.map +1 -0
- package/dist/plain.js +57 -0
- package/dist/plain.js.map +1 -0
- package/dist/player.d.ts +33 -10
- package/dist/player.js +159 -0
- package/dist/player.js.map +1 -0
- package/dist/portals.d.ts +1 -1
- package/dist/portals.js +194 -0
- package/dist/portals.js.map +1 -0
- package/dist/prefabs.d.ts +25 -2
- package/dist/prefabs.js +239 -0
- package/dist/prefabs.js.map +1 -0
- package/dist/presetBuildings.js +58 -0
- package/dist/presetBuildings.js.map +1 -0
- package/dist/presetFoliage.d.ts +27 -0
- package/dist/presetFoliage.js +98 -0
- package/dist/presetFoliage.js.map +1 -0
- package/dist/presetGround.js +65 -0
- package/dist/presetGround.js.map +1 -0
- package/dist/proxies.d.ts +12 -0
- package/dist/proxies.js +79 -0
- package/dist/proxies.js.map +1 -0
- package/dist/query.d.ts +56 -0
- package/dist/query.js +88 -0
- package/dist/query.js.map +1 -0
- package/dist/races.d.ts +15 -14
- package/dist/races.js +172 -0
- package/dist/races.js.map +1 -0
- package/dist/randomGroups.js +91 -0
- package/dist/randomGroups.js.map +1 -0
- package/dist/registry.d.ts +117 -53
- package/dist/registry.js +323 -0
- package/dist/registry.js.map +1 -0
- package/dist/remains.d.ts +11 -9
- package/dist/remains.js +188 -0
- package/dist/remains.js.map +1 -0
- package/dist/replication.d.ts +12 -7
- package/dist/replication.js +100 -0
- package/dist/replication.js.map +1 -0
- package/dist/rng.d.ts +2 -0
- package/dist/rng.js +40 -0
- package/dist/rng.js.map +1 -0
- package/dist/rules.d.ts +186 -99
- package/dist/rules.js +179 -0
- package/dist/rules.js.map +1 -0
- package/dist/rulesCheck.d.ts +5 -4
- package/dist/rulesCheck.js +693 -0
- package/dist/rulesCheck.js.map +1 -0
- package/dist/runes.d.ts +2 -2
- package/dist/runes.js +80 -0
- package/dist/runes.js.map +1 -0
- package/dist/save.d.ts +37 -12
- package/dist/save.js +532 -0
- package/dist/save.js.map +1 -0
- package/dist/scenario.d.ts +2 -2
- package/dist/scenario.js +99 -0
- package/dist/scenario.js.map +1 -0
- package/dist/scheduler.d.ts +3 -0
- package/dist/scheduler.js +156 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/score.d.ts +19 -19
- package/dist/score.js +709 -0
- package/dist/score.js.map +1 -0
- package/dist/sequences.d.ts +10 -6
- package/dist/sequences.js +206 -0
- package/dist/sequences.js.map +1 -0
- package/dist/server/doorman.js +154 -0
- package/dist/server/doorman.js.map +1 -0
- package/dist/server/gameServer.d.ts +16 -1
- package/dist/server/gameServer.js +643 -0
- package/dist/server/gameServer.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/lobby.js +199 -0
- package/dist/server/lobby.js.map +1 -0
- package/dist/server/log.js +18 -0
- package/dist/server/log.js.map +1 -0
- package/dist/server/match.js +267 -0
- package/dist/server/match.js.map +1 -0
- package/dist/server/mode.d.ts +18 -9
- package/dist/server/mode.js +220 -0
- package/dist/server/mode.js.map +1 -0
- package/dist/{navigation-worker.js → server/navigation-worker.js} +1 -1
- package/dist/server/navigation-worker.js.map +1 -0
- package/dist/server/navigation.js +39 -0
- package/dist/server/navigation.js.map +1 -0
- package/dist/server/ops.d.ts +4 -0
- package/dist/server/ops.js +137 -0
- package/dist/server/ops.js.map +1 -0
- package/dist/server/packs.d.ts +3 -3
- package/dist/server/packs.js +32 -0
- package/dist/server/packs.js.map +1 -0
- package/dist/server/ratings.d.ts +5 -0
- package/dist/server/ratings.js +104 -0
- package/dist/server/ratings.js.map +1 -0
- package/dist/server/relay.d.ts +12 -2
- package/dist/server/relay.js +144 -0
- package/dist/server/relay.js.map +1 -0
- package/dist/server/rooms.d.ts +9 -2
- package/dist/server/rooms.js +442 -0
- package/dist/server/rooms.js.map +1 -0
- package/dist/server/seat.js +116 -0
- package/dist/server/seat.js.map +1 -0
- package/dist/server/seatSettings.d.ts +3 -3
- package/dist/server/seatSettings.js +63 -0
- package/dist/server/seatSettings.js.map +1 -0
- package/dist/server/secret.js +13 -0
- package/dist/server/secret.js.map +1 -0
- package/dist/server/start.d.ts +13 -6
- package/dist/server/start.js +76 -0
- package/dist/server/start.js.map +1 -0
- package/dist/server/websocket.d.ts +9 -1
- package/dist/server/websocket.js +306 -0
- package/dist/server/websocket.js.map +1 -0
- package/dist/server.js +15 -2861
- package/dist/session/can.js +26 -0
- package/dist/session/can.js.map +1 -0
- package/dist/session/commandReplay.d.ts +8 -2
- package/dist/session/commandReplay.js +293 -0
- package/dist/session/commandReplay.js.map +1 -0
- package/dist/session/debug.js +151 -0
- package/dist/session/debug.js.map +1 -0
- package/dist/session/delay.js +62 -0
- package/dist/session/delay.js.map +1 -0
- package/dist/session/delta.d.ts +14 -1
- package/dist/session/delta.js +407 -0
- package/dist/session/delta.js.map +1 -0
- package/dist/session/entityState.js +415 -0
- package/dist/session/entityState.js.map +1 -0
- package/dist/session/local.d.ts +47 -5
- package/dist/session/local.js +670 -0
- package/dist/session/local.js.map +1 -0
- package/dist/session/memory.d.ts +20 -2
- package/dist/session/memory.js +131 -0
- package/dist/session/memory.js.map +1 -0
- package/dist/session/protocol.d.ts +325 -31
- package/dist/session/protocol.js +55 -0
- package/dist/session/protocol.js.map +1 -0
- package/dist/session/remote.d.ts +74 -12
- package/dist/session/remote.js +1394 -0
- package/dist/session/remote.js.map +1 -0
- package/dist/session/replay.d.ts +11 -2
- package/dist/session/replay.js +160 -0
- package/dist/session/replay.js.map +1 -0
- package/dist/session/seed.js +12 -0
- package/dist/session/seed.js.map +1 -0
- package/dist/session/share.d.ts +5 -1
- package/dist/session/share.js +94 -0
- package/dist/session/share.js.map +1 -0
- package/dist/session/socket.d.ts +1 -1
- package/dist/session/socket.js +29 -0
- package/dist/session/socket.js.map +1 -0
- package/dist/session/wire.d.ts +5 -0
- package/dist/session/wire.js +84 -0
- package/dist/session/wire.js.map +1 -0
- package/dist/session/worker.d.ts +77 -12
- package/dist/session/worker.js +499 -0
- package/dist/session/worker.js.map +1 -0
- package/dist/shared.d.ts +31 -5
- package/dist/shared.js +166 -0
- package/dist/shared.js.map +1 -0
- package/dist/sides.d.ts +9 -3
- package/dist/sides.js +149 -0
- package/dist/sides.js.map +1 -0
- package/dist/sight.js +85 -0
- package/dist/sight.js.map +1 -0
- package/dist/simulate-worker.js +1 -1
- package/dist/simulate.d.ts +12 -8
- package/dist/{simulate-oXBiw1Ff.js → simulate.js} +10 -11
- package/dist/simulate.js.map +1 -0
- package/dist/smart.js +77 -0
- package/dist/smart.js.map +1 -0
- package/dist/spatial.d.ts +32 -16
- package/dist/spatial.js +234 -0
- package/dist/spatial.js.map +1 -0
- package/dist/spawning.d.ts +1 -0
- package/dist/spawning.js +224 -0
- package/dist/spawning.js.map +1 -0
- package/dist/stances.d.ts +6 -3
- package/dist/stances.js +169 -0
- package/dist/stances.js.map +1 -0
- package/dist/start.d.ts +10 -10
- package/dist/start.js +75 -0
- package/dist/start.js.map +1 -0
- package/dist/stats.d.ts +42 -16
- package/dist/stats.js +290 -0
- package/dist/stats.js.map +1 -0
- package/dist/summoned.d.ts +52 -0
- package/dist/summoned.js +38 -0
- package/dist/summoned.js.map +1 -0
- package/dist/summons.d.ts +10 -13
- package/dist/summons.js +271 -0
- package/dist/summons.js.map +1 -0
- package/dist/systems/abilities.js +224 -0
- package/dist/systems/abilities.js.map +1 -0
- package/dist/systems/combat.d.ts +35 -1
- package/dist/systems/combat.js +677 -0
- package/dist/systems/combat.js.map +1 -0
- package/dist/systems/combatTold.d.ts +3 -1
- package/dist/systems/combatTold.js +85 -0
- package/dist/systems/combatTold.js.map +1 -0
- package/dist/systems/construction.js +80 -0
- package/dist/systems/construction.js.map +1 -0
- package/dist/systems/economy.d.ts +0 -6
- package/dist/systems/economy.js +128 -0
- package/dist/systems/economy.js.map +1 -0
- package/dist/systems/fog.d.ts +1 -1
- package/dist/systems/fog.js +120 -0
- package/dist/systems/fog.js.map +1 -0
- package/dist/systems/life.js +32 -0
- package/dist/systems/life.js.map +1 -0
- package/dist/systems/movement.d.ts +24 -12
- package/dist/systems/movement.js +1247 -0
- package/dist/systems/movement.js.map +1 -0
- package/dist/systems/orders.js +64 -0
- package/dist/systems/orders.js.map +1 -0
- package/dist/systems/production.js +322 -0
- package/dist/systems/production.js.map +1 -0
- package/dist/systems/productionQueue.js +120 -0
- package/dist/systems/productionQueue.js.map +1 -0
- package/dist/systems/victory.d.ts +1 -1
- package/dist/systems/victory.js +56 -0
- package/dist/systems/victory.js.map +1 -0
- package/dist/talk.d.ts +6 -6
- package/dist/talk.js +153 -0
- package/dist/talk.js.map +1 -0
- package/dist/targeting.d.ts +142 -0
- package/dist/targeting.js +49 -0
- package/dist/targeting.js.map +1 -0
- package/dist/tech.d.ts +20 -3
- package/dist/tech.js +239 -0
- package/dist/tech.js.map +1 -0
- package/dist/techTree.d.ts +7 -0
- package/dist/techTree.js +90 -0
- package/dist/techTree.js.map +1 -0
- package/dist/terrain.d.ts +59 -7
- package/dist/terrain.js +389 -0
- package/dist/terrain.js.map +1 -0
- package/dist/terrainSave.d.ts +6 -0
- package/dist/terrainSave.js +74 -0
- package/dist/terrainSave.js.map +1 -0
- package/dist/testing.d.ts +8 -7
- package/dist/testing.js +13 -739
- package/dist/testing.js.map +1 -1
- package/dist/tiles.d.ts +1 -1
- package/dist/tiles.js +43 -0
- package/dist/tiles.js.map +1 -0
- package/dist/timers.d.ts +29 -4
- package/dist/timers.js +242 -0
- package/dist/timers.js.map +1 -0
- package/dist/tomes.d.ts +4 -4
- package/dist/tomes.js +94 -0
- package/dist/tomes.js.map +1 -0
- package/dist/trade.js +103 -0
- package/dist/trade.js.map +1 -0
- package/dist/transport.d.ts +6 -1
- package/dist/transport.js +281 -0
- package/dist/transport.js.map +1 -0
- package/dist/tunables.d.ts +10 -3
- package/dist/tunables.js +56 -0
- package/dist/tunables.js.map +1 -0
- package/dist/unitResources.d.ts +67 -0
- package/dist/unitResources.js +266 -0
- package/dist/unitResources.js.map +1 -0
- package/dist/upkeep.d.ts +6 -1
- package/dist/upkeep.js +26 -0
- package/dist/upkeep.js.map +1 -0
- package/dist/veterancy.d.ts +8 -3
- package/dist/veterancy.js +131 -0
- package/dist/veterancy.js.map +1 -0
- package/dist/vision.d.ts +36 -0
- package/dist/vision.js +133 -0
- package/dist/vision.js.map +1 -0
- package/dist/walls.d.ts +1 -1
- package/dist/walls.js +278 -0
- package/dist/walls.js.map +1 -0
- package/dist/world.d.ts +606 -88
- package/dist/world.js +1263 -0
- package/dist/world.js.map +1 -0
- package/package.json +2 -2
- package/dist/ai.js.map +0 -1
- package/dist/attributes.d.ts +0 -66
- package/dist/brain-BvVdUhX8.js +0 -7659
- package/dist/brain-BvVdUhX8.js.map +0 -1
- package/dist/builds-C6LfgvEU.js +0 -460
- package/dist/builds-C6LfgvEU.js.map +0 -1
- package/dist/coreComponents-syuh2fPG.js +0 -5149
- package/dist/coreComponents-syuh2fPG.js.map +0 -1
- package/dist/debugCommands-CzjrSm66.js +0 -14739
- package/dist/debugCommands-CzjrSm66.js.map +0 -1
- package/dist/determinism-BCymQwiE.js +0 -518
- package/dist/determinism-BCymQwiE.js.map +0 -1
- package/dist/game-DiJEN-Ks.js +0 -5800
- package/dist/game-DiJEN-Ks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/inspect-D9dWHmZ0.js.map +0 -1
- package/dist/local-Dkovq42k.js +0 -1379
- package/dist/local-Dkovq42k.js.map +0 -1
- package/dist/missionRules-C-bGxm0Q.js.map +0 -1
- package/dist/navigation-worker.js.map +0 -1
- package/dist/packs-g5thvmGn.js +0 -3199
- package/dist/packs-g5thvmGn.js.map +0 -1
- package/dist/profiles-B3NS9jwk.js.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/simulate-oXBiw1Ff.js.map +0 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { navigationWorker } from "../navigation/builds.js";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
//#region src/server/navigation.ts
|
|
5
|
+
/**
|
|
6
|
+
* A builder of navigations in a worker thread of this process (S37): what a server builds with, so that the world of
|
|
7
|
+
* one game being made does not hold up the ticks of the others (two seconds for a map of 512 cells a side). One
|
|
8
|
+
* thread, made the first time and kept, which keeps the process alive only while it builds. Nothing where the
|
|
9
|
+
* worker's file is not there: in this repository, which runs the engine as it is written, it builds on the thread
|
|
10
|
+
* that asks
|
|
11
|
+
*
|
|
12
|
+
* @beta
|
|
13
|
+
*/
|
|
14
|
+
function nodeNavigationBuilder() {
|
|
15
|
+
const script = new URL("navigation-worker.js", import.meta.url);
|
|
16
|
+
if (script.protocol !== "file:" || !existsSync(fileURLToPath(script))) return void 0;
|
|
17
|
+
let made;
|
|
18
|
+
let pending = 0;
|
|
19
|
+
return async (build) => {
|
|
20
|
+
made ??= import("node:worker_threads").then(({ Worker }) => {
|
|
21
|
+
const worker = new Worker(script);
|
|
22
|
+
return {
|
|
23
|
+
worker,
|
|
24
|
+
builder: navigationWorker(worker)
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
const { worker, builder } = await made;
|
|
28
|
+
if (pending++ === 0) worker.ref();
|
|
29
|
+
try {
|
|
30
|
+
return await builder(build);
|
|
31
|
+
} finally {
|
|
32
|
+
if (--pending === 0) worker.unref();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { nodeNavigationBuilder };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.js","names":[],"sources":["../../src/server/navigation.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { type NavigationBuilder, navigationWorker } from '#engine/navigation/builds'\n\n/**\n * A builder of navigations in a worker thread of this process (S37): what a server builds with, so that the world of\n * one game being made does not hold up the ticks of the others (two seconds for a map of 512 cells a side). One\n * thread, made the first time and kept, which keeps the process alive only while it builds. Nothing where the\n * worker's file is not there: in this repository, which runs the engine as it is written, it builds on the thread\n * that asks\n *\n * @beta\n */\nexport function nodeNavigationBuilder(): NavigationBuilder | undefined {\n\t// the worker's file sits next to this one in the build: named so, a bundler leaves the address alone\n\tconst file = 'navigation-worker.js'\n\tconst script = new URL(file, import.meta.url)\n\tif (script.protocol !== 'file:' || !existsSync(fileURLToPath(script))) return undefined\n\tlet made: Promise<{ worker: { ref(): void; unref(): void }; builder: NavigationBuilder }> | undefined\n\tlet pending = 0\n\treturn async (build) => {\n\t\tmade ??= import('node:worker_threads').then(({ Worker }) => {\n\t\t\tconst worker = new Worker(script)\n\t\t\treturn { worker, builder: navigationWorker(worker as never) }\n\t\t})\n\t\tconst { worker, builder } = await made\n\t\t// it keeps the process alive while it builds, and not after\n\t\tif (pending++ === 0) worker.ref()\n\t\ttry {\n\t\t\treturn await builder(build)\n\t\t} finally {\n\t\t\tif (--pending === 0) worker.unref()\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,wBAAuD;CAGtE,MAAM,SAAS,IAAI,IAAI,wBAAM,YAAY,GAAG;CAC5C,IAAI,OAAO,aAAa,WAAW,CAAC,WAAW,cAAc,MAAM,CAAC,GAAG,OAAO,KAAA;CAC9E,IAAI;CACJ,IAAI,UAAU;CACd,OAAO,OAAO,UAAU;EACvB,SAAS,OAAO,sBAAsB,CAAC,MAAM,EAAE,aAAa;GAC3D,MAAM,SAAS,IAAI,OAAO,MAAM;GAChC,OAAO;IAAE;IAAQ,SAAS,iBAAiB,MAAe;GAAE;EAC7D,CAAC;EACD,MAAM,EAAE,QAAQ,YAAY,MAAM;EAElC,IAAI,cAAc,GAAG,OAAO,IAAI;EAChC,IAAI;GACH,OAAO,MAAM,QAAQ,KAAK;EAC3B,UAAU;GACT,IAAI,EAAE,YAAY,GAAG,OAAO,MAAM;EACnC;CACD;AACD"}
|
package/dist/server/ops.d.ts
CHANGED
|
@@ -21,15 +21,19 @@ export interface ServerMetrics {
|
|
|
21
21
|
};
|
|
22
22
|
/** Seats held by someone connected, and those watching */
|
|
23
23
|
players: number;
|
|
24
|
+
/** Connections watching the game without a seat */
|
|
24
25
|
watching: number;
|
|
25
26
|
/** Connections open, seated or not */
|
|
26
27
|
connections: number;
|
|
27
28
|
/** Ticks of the simulation stepped since it started, and the seconds they took; how long the last one took */
|
|
28
29
|
ticks: number;
|
|
30
|
+
/** Total seconds the simulation ticks have taken since the server started */
|
|
29
31
|
tickSeconds: number;
|
|
32
|
+
/** Seconds the most recent tick took */
|
|
30
33
|
lastTickSeconds: number;
|
|
31
34
|
/** Rounds of sending everyone what changed, and the seconds they took */
|
|
32
35
|
sends: number;
|
|
36
|
+
/** Total seconds the rounds of sending have taken since the server started */
|
|
33
37
|
sendSeconds: number;
|
|
34
38
|
/** Bytes sent to those connected now, over the wire */
|
|
35
39
|
bytesSent: number;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import "../session/protocol.js";
|
|
2
|
+
import { sameSecret } from "./secret.js";
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/server/ops.ts
|
|
6
|
+
/** The numbers of a process: its memory */
|
|
7
|
+
var memory = () => {
|
|
8
|
+
const { rss, heapUsed } = process.memoryUsage();
|
|
9
|
+
return {
|
|
10
|
+
rss,
|
|
11
|
+
heapUsed
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The numbers of the rooms of a process, in Prometheus's text: each by its room, where there is more than one
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
function metricsText(rooms) {
|
|
20
|
+
const lines = [];
|
|
21
|
+
const each = (name, help, type, value) => {
|
|
22
|
+
lines.push(`# HELP vg_${name} ${help}`, `# TYPE vg_${name} ${type}`);
|
|
23
|
+
for (const [room, metrics] of rooms) lines.push(`vg_${name}${room ? `{room=${JSON.stringify(room)}}` : ""} ${value(metrics)}`);
|
|
24
|
+
};
|
|
25
|
+
each("up", "Whether it takes new games: 0 once it is stopping", "gauge", (m) => m.draining ? 0 : 1);
|
|
26
|
+
each("games_running", "Games running now", "gauge", (m) => m.games.running);
|
|
27
|
+
each("games_played_total", "Games played to their end", "counter", (m) => m.games.played);
|
|
28
|
+
each("players", "Seats held by someone connected", "gauge", (m) => m.players);
|
|
29
|
+
each("watching", "Connections watching a game", "gauge", (m) => m.watching);
|
|
30
|
+
each("connections", "Connections open", "gauge", (m) => m.connections);
|
|
31
|
+
each("ticks_total", "Ticks of the simulation stepped", "counter", (m) => m.ticks);
|
|
32
|
+
each("tick_seconds_total", "Seconds the ticks took", "counter", (m) => round(m.tickSeconds));
|
|
33
|
+
each("last_tick_seconds", "How long the last tick took", "gauge", (m) => round(m.lastTickSeconds));
|
|
34
|
+
each("sends_total", "Rounds of sending what changed", "counter", (m) => m.sends);
|
|
35
|
+
each("send_seconds_total", "Seconds the sending took", "counter", (m) => round(m.sendSeconds));
|
|
36
|
+
each("bytes_sent", "Bytes sent to those connected now", "gauge", (m) => m.bytesSent);
|
|
37
|
+
each("uptime_seconds", "Seconds since it started", "gauge", (m) => round(m.uptime));
|
|
38
|
+
const { rss, heapUsed } = memory();
|
|
39
|
+
lines.push("# HELP vg_protocol_version The version of the wire its players have to speak", "# TYPE vg_protocol_version gauge", `vg_protocol_version 13`, "# HELP vg_memory_bytes Memory of the process", "# TYPE vg_memory_bytes gauge", `vg_memory_bytes{kind="rss"} ${rss}`, `vg_memory_bytes{kind="heap"} ${heapUsed}`);
|
|
40
|
+
return `${lines.join("\n")}\n`;
|
|
41
|
+
}
|
|
42
|
+
var round = (value) => Math.round(value * 1e6) / 1e6;
|
|
43
|
+
/**
|
|
44
|
+
* Answers the health and numbers addresses for the rooms of a process; false for any other request, which goes to
|
|
45
|
+
* what answered it before. `draining` is whether the process takes no new games
|
|
46
|
+
*/
|
|
47
|
+
function answerOps(request, response, options, state, games, ratings) {
|
|
48
|
+
const { pathname, searchParams } = new URL(request.url ?? "/", "http://server");
|
|
49
|
+
const best = options?.ratings ?? "/ratings";
|
|
50
|
+
if (best && ratings?.ratings && pathname === best && (request.method === "GET" || request.method === "HEAD")) {
|
|
51
|
+
const mode = searchParams.get("mode") ?? ratings.modes[0] ?? "";
|
|
52
|
+
const count = Math.min(Math.max(Number(searchParams.get("n")) || 10, 1), 100);
|
|
53
|
+
const player = searchParams.get("player");
|
|
54
|
+
const top = ratings.ratings.top?.(mode, count) ?? [];
|
|
55
|
+
const own = player ? ratings.ratings.placeOf?.(mode, player) : void 0;
|
|
56
|
+
response.writeHead(200, {
|
|
57
|
+
"content-type": "application/json",
|
|
58
|
+
"cache-control": "no-store",
|
|
59
|
+
"access-control-allow-origin": "*"
|
|
60
|
+
});
|
|
61
|
+
response.end(JSON.stringify({
|
|
62
|
+
mode,
|
|
63
|
+
modes: ratings.modes,
|
|
64
|
+
top,
|
|
65
|
+
...own && { player: own }
|
|
66
|
+
}));
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
const health = options?.health ?? "/health";
|
|
70
|
+
const metrics = options?.metrics ?? "/metrics";
|
|
71
|
+
const list = options?.games ?? "/games";
|
|
72
|
+
if (request.method !== "GET" && request.method !== "HEAD") return false;
|
|
73
|
+
if (list && games && pathname === list) {
|
|
74
|
+
response.writeHead(200, {
|
|
75
|
+
"content-type": "application/json",
|
|
76
|
+
"cache-control": "no-store",
|
|
77
|
+
"access-control-allow-origin": "*"
|
|
78
|
+
});
|
|
79
|
+
response.end(JSON.stringify({
|
|
80
|
+
draining: state().draining,
|
|
81
|
+
...games()
|
|
82
|
+
}));
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (health && pathname === health) {
|
|
86
|
+
const { draining } = state();
|
|
87
|
+
response.writeHead(draining ? 503 : 200, {
|
|
88
|
+
"content-type": "application/json",
|
|
89
|
+
"cache-control": "no-store"
|
|
90
|
+
});
|
|
91
|
+
response.end(JSON.stringify({
|
|
92
|
+
ok: !draining,
|
|
93
|
+
draining
|
|
94
|
+
}));
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (metrics && pathname === metrics) {
|
|
98
|
+
if (options?.metricsKey && !sameSecret(request.headers.authorization ?? "", `Bearer ${options.metricsKey}`)) {
|
|
99
|
+
response.writeHead(401, {
|
|
100
|
+
"content-type": "text/plain",
|
|
101
|
+
"www-authenticate": "Bearer"
|
|
102
|
+
});
|
|
103
|
+
response.end("The numbers take a key\n");
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const { rooms, draining } = state();
|
|
107
|
+
if (searchParams.has("json")) {
|
|
108
|
+
response.writeHead(200, {
|
|
109
|
+
"content-type": "application/json",
|
|
110
|
+
"cache-control": "no-store"
|
|
111
|
+
});
|
|
112
|
+
response.end(JSON.stringify({
|
|
113
|
+
draining,
|
|
114
|
+
protocol: 13,
|
|
115
|
+
memory: memory(),
|
|
116
|
+
rooms: Object.fromEntries(rooms)
|
|
117
|
+
}));
|
|
118
|
+
} else {
|
|
119
|
+
response.writeHead(200, {
|
|
120
|
+
"content-type": "text/plain; version=0.0.4",
|
|
121
|
+
"cache-control": "no-store"
|
|
122
|
+
});
|
|
123
|
+
response.end(metricsText(rooms));
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
/** A game's result as a file of its own in `dir` (`GameServerOptions.resultsDir`): by when it ended and its mode */
|
|
130
|
+
async function writeResult(dir, result) {
|
|
131
|
+
await mkdir(dir, { recursive: true });
|
|
132
|
+
await writeFile(join(dir, `${Date.now()}-${result.mode.replace(/[^\w-]/g, "_")}.json`), JSON.stringify(result, null, 2));
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { answerOps, metricsText, writeResult };
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ops.js","names":[],"sources":["../../src/server/ops.ts"],"sourcesContent":["import type { Ratings } from './ratings'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { GameResult } from '#engine/game'\nimport { type GameKindListing, type GameListing, PROTOCOL_VERSION } from '#engine/session/protocol'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { sameSecret } from './secret'\n\n/*\n * A server to run in production (S58): what the HTTP server the sockets are on answers besides them. An address that\n * says whether it is well (`/health`: 200 while it takes games, 503 once it is stopping, for a load balancer to send\n * no one new) and one with its numbers (`/metrics`: as Prometheus reads them, or as JSON with `?json`)\n */\n\n/**\n * What a game server says of itself: the numbers of `/metrics` (`GameServer.metrics`)\n *\n * @beta\n */\nexport interface ServerMetrics {\n\t/** In its lobby, playing, or showing a game that ended */\n\tphase: 'lobby' | 'game' | 'ended'\n\t/** It takes no new games: it is stopping, once the running one is over (`close({ drain })`) */\n\tdraining: boolean\n\t/** Seconds since it started */\n\tuptime: number\n\t/** Games running now (0 or 1 for one server), and played to their end since it started */\n\tgames: { running: number; played: number }\n\t/** Seats held by someone connected, and those watching */\n\tplayers: number\n\t/** Connections watching the game without a seat */\n\twatching: number\n\t/** Connections open, seated or not */\n\tconnections: number\n\t/** Ticks of the simulation stepped since it started, and the seconds they took; how long the last one took */\n\tticks: number\n\t/** Total seconds the simulation ticks have taken since the server started */\n\ttickSeconds: number\n\t/** Seconds the most recent tick took */\n\tlastTickSeconds: number\n\t/** Rounds of sending everyone what changed, and the seconds they took */\n\tsends: number\n\t/** Total seconds the rounds of sending have taken since the server started */\n\tsendSeconds: number\n\t/** Bytes sent to those connected now, over the wire */\n\tbytesSent: number\n}\n\n/**\n * Where the addresses are, and who may read the numbers (`GameServerOptions.http`)\n *\n * @beta\n */\nexport interface ServerHttpOptions {\n\t/** The address that says whether it is well. `/health` when left out; false for none */\n\thealth?: string | false\n\t/** The address of its numbers. `/metrics` when left out; false for none */\n\tmetrics?: string | false\n\t/**\n\t * The address of the list of its games, for a page to find one (`GameListing`, JSON, readable from any page). `/games`\n\t * when left out; false for none\n\t */\n\tgames?: string | false\n\t/**\n\t * The address of the best of each mode (`/ratings?mode=melee&n=10&player=<rated id>`: the top and the player's own\n\t * place, JSON, readable from any page), where the server keeps ratings. `/ratings` when left out; false for none @beta\n\t */\n\tratings?: string | false\n\t/**\n\t * What reading the numbers takes (`Authorization: Bearer <key>`): they say how many play, and when. Anyone who can\n\t * reach the address when left out: keep it behind the proxy then\n\t */\n\tmetricsKey?: string\n}\n\n/** The numbers of a process: its memory */\nconst memory = () => {\n\tconst { rss, heapUsed } = process.memoryUsage()\n\treturn { rss, heapUsed }\n}\n\n/**\n * The numbers of the rooms of a process, in Prometheus's text: each by its room, where there is more than one\n *\n * @internal\n */\nexport function metricsText(rooms: ReadonlyMap<string, ServerMetrics>): string {\n\tconst lines: string[] = []\n\tconst each = (name: string, help: string, type: 'gauge' | 'counter', value: (metrics: ServerMetrics) => number) => {\n\t\tlines.push(`# HELP vg_${name} ${help}`, `# TYPE vg_${name} ${type}`)\n\t\tfor (const [room, metrics] of rooms) lines.push(`vg_${name}${room ? `{room=${JSON.stringify(room)}}` : ''} ${value(metrics)}`)\n\t}\n\teach('up', 'Whether it takes new games: 0 once it is stopping', 'gauge', (m) => (m.draining ? 0 : 1))\n\teach('games_running', 'Games running now', 'gauge', (m) => m.games.running)\n\teach('games_played_total', 'Games played to their end', 'counter', (m) => m.games.played)\n\teach('players', 'Seats held by someone connected', 'gauge', (m) => m.players)\n\teach('watching', 'Connections watching a game', 'gauge', (m) => m.watching)\n\teach('connections', 'Connections open', 'gauge', (m) => m.connections)\n\teach('ticks_total', 'Ticks of the simulation stepped', 'counter', (m) => m.ticks)\n\teach('tick_seconds_total', 'Seconds the ticks took', 'counter', (m) => round(m.tickSeconds))\n\teach('last_tick_seconds', 'How long the last tick took', 'gauge', (m) => round(m.lastTickSeconds))\n\teach('sends_total', 'Rounds of sending what changed', 'counter', (m) => m.sends)\n\teach('send_seconds_total', 'Seconds the sending took', 'counter', (m) => round(m.sendSeconds))\n\teach('bytes_sent', 'Bytes sent to those connected now', 'gauge', (m) => m.bytesSent)\n\teach('uptime_seconds', 'Seconds since it started', 'gauge', (m) => round(m.uptime))\n\tconst { rss, heapUsed } = memory()\n\tlines.push(\n\t\t// which version of the wire it speaks: a deploy of mixed versions shows as two values\n\t\t'# HELP vg_protocol_version The version of the wire its players have to speak',\n\t\t'# TYPE vg_protocol_version gauge',\n\t\t`vg_protocol_version ${PROTOCOL_VERSION}`,\n\t\t'# HELP vg_memory_bytes Memory of the process',\n\t\t'# TYPE vg_memory_bytes gauge',\n\t\t`vg_memory_bytes{kind=\"rss\"} ${rss}`,\n\t\t`vg_memory_bytes{kind=\"heap\"} ${heapUsed}`\n\t)\n\treturn `${lines.join('\\n')}\\n`\n}\n\nconst round = (value: number) => Math.round(value * 1e6) / 1e6\n\n/**\n * Answers the health and numbers addresses for the rooms of a process; false for any other request, which goes to\n * what answered it before. `draining` is whether the process takes no new games\n */\nexport function answerOps(\n\trequest: IncomingMessage,\n\tresponse: ServerResponse,\n\toptions: ServerHttpOptions | undefined,\n\tstate: () => { draining: boolean; rooms: ReadonlyMap<string, ServerMetrics> },\n\tgames?: () => { games: GameListing[]; kinds?: GameKindListing[] },\n\tratings?: { ratings?: Ratings; modes: readonly string[] }\n): boolean {\n\tconst { pathname, searchParams } = new URL(request.url ?? '/', 'http://server')\n\tconst best = options?.ratings ?? '/ratings'\n\tif (best && ratings?.ratings && pathname === best && (request.method === 'GET' || request.method === 'HEAD')) {\n\t\tconst mode = searchParams.get('mode') ?? ratings.modes[0] ?? ''\n\t\tconst count = Math.min(Math.max(Number(searchParams.get('n')) || 10, 1), 100)\n\t\tconst player = searchParams.get('player')\n\t\tconst top = ratings.ratings.top?.(mode, count) ?? []\n\t\tconst own = player ? ratings.ratings.placeOf?.(mode, player) : undefined\n\t\t// read by the pages of a game, wherever they are served from; names are what players chose to be seen by\n\t\tresponse.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store', 'access-control-allow-origin': '*' })\n\t\tresponse.end(JSON.stringify({ mode, modes: ratings.modes, top, ...(own && { player: own }) }))\n\t\treturn true\n\t}\n\tconst health = options?.health ?? '/health'\n\tconst metrics = options?.metrics ?? '/metrics'\n\tconst list = options?.games ?? '/games'\n\tif (request.method !== 'GET' && request.method !== 'HEAD') return false\n\tif (list && games && pathname === list) {\n\t\t// read by the pages of a game, wherever they are served from\n\t\tresponse.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store', 'access-control-allow-origin': '*' })\n\t\tresponse.end(JSON.stringify({ draining: state().draining, ...games() }))\n\t\treturn true\n\t}\n\tif (health && pathname === health) {\n\t\tconst { draining } = state()\n\t\tresponse.writeHead(draining ? 503 : 200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n\t\tresponse.end(JSON.stringify({ ok: !draining, draining }))\n\t\treturn true\n\t}\n\tif (metrics && pathname === metrics) {\n\t\tif (options?.metricsKey && !sameSecret(request.headers.authorization ?? '', `Bearer ${options.metricsKey}`)) {\n\t\t\tresponse.writeHead(401, { 'content-type': 'text/plain', 'www-authenticate': 'Bearer' })\n\t\t\tresponse.end('The numbers take a key\\n')\n\t\t\treturn true\n\t\t}\n\t\tconst { rooms, draining } = state()\n\t\tif (searchParams.has('json')) {\n\t\t\tresponse.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n\t\t\tresponse.end(JSON.stringify({ draining, protocol: PROTOCOL_VERSION, memory: memory(), rooms: Object.fromEntries(rooms) }))\n\t\t} else {\n\t\t\tresponse.writeHead(200, { 'content-type': 'text/plain; version=0.0.4', 'cache-control': 'no-store' })\n\t\t\tresponse.end(metricsText(rooms))\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\n/** A game's result as a file of its own in `dir` (`GameServerOptions.resultsDir`): by when it ended and its mode */\nexport async function writeResult(dir: string, result: GameResult): Promise<void> {\n\tawait mkdir(dir, { recursive: true })\n\tawait writeFile(join(dir, `${Date.now()}-${result.mode.replace(/[^\\w-]/g, '_')}.json`), JSON.stringify(result, null, 2))\n}\n"],"mappings":"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"}
|
package/dist/server/packs.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Rules } from '#engine/game';
|
|
2
2
|
import { type PackMod } from '#engine/packs';
|
|
3
3
|
/**
|
|
4
4
|
* The packs of content of a server, read from their files (`packs/<id>/pack.json`) in the order named: that order is
|
|
5
|
-
* the game's, as a page's `
|
|
5
|
+
* the game's, as a page's `createGame({ contentPacks })` is
|
|
6
6
|
*
|
|
7
7
|
* @beta
|
|
8
8
|
*/
|
|
@@ -12,4 +12,4 @@ export declare function readPacks(files: readonly string[]): PackMod[];
|
|
|
12
12
|
*
|
|
13
13
|
* @beta
|
|
14
14
|
*/
|
|
15
|
-
export declare function
|
|
15
|
+
export declare function mergePackFiles(game: Rules, files: readonly string[]): Rules;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { composeGame } from "../mods.js";
|
|
2
|
+
import { PackError, checkPackSchema, packFromJson } from "../packs.js";
|
|
3
|
+
import pack_schema_default from "../pack.schema.js";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
//#region src/server/packs.ts
|
|
6
|
+
/**
|
|
7
|
+
* The packs of content of a server, read from their files (`packs/<id>/pack.json`) in the order named: that order is
|
|
8
|
+
* the game's, as a page's `createGame({ contentPacks })` is
|
|
9
|
+
*
|
|
10
|
+
* @beta
|
|
11
|
+
*/
|
|
12
|
+
function readPacks(files) {
|
|
13
|
+
return files.map((file) => {
|
|
14
|
+
const text = readFileSync(file, "utf8");
|
|
15
|
+
const pack = packFromJson(text, file);
|
|
16
|
+
const wrong = checkPackSchema(JSON.parse(text), pack_schema_default);
|
|
17
|
+
if (wrong) throw new PackError(`${file}: ${wrong}`);
|
|
18
|
+
return pack;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A server's game with its packs of content composed in (`readPacks`, `composeGame`): the game as is with none
|
|
23
|
+
*
|
|
24
|
+
* @beta
|
|
25
|
+
*/
|
|
26
|
+
function mergePackFiles(game, files) {
|
|
27
|
+
return files.length ? composeGame(game, readPacks(files)) : game;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { mergePackFiles, readPacks };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=packs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packs.js","names":["packSchema"],"sources":["../../src/server/packs.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport type { Rules } from '#engine/game'\nimport { composeGame } from '#engine/mods'\nimport { checkPackSchema, PackError, packFromJson, type PackMod } from '#engine/packs'\n// the pack's schema, in the server's bundle: a server reads packs where a page fetches it with them\nimport packSchema from '../pack.schema.json'\n\n/*\n * A server's packs of content (campaign plan RC6): read from its files, in the order they are named, and composed\n * into the game before any room is made. The host's packs are the game's: a player whose page has others is told as\n * they join (the welcome names the game, `melee+<pack>`, and its hash).\n */\n\n/**\n * The packs of content of a server, read from their files (`packs/<id>/pack.json`) in the order named: that order is\n * the game's, as a page's `createGame({ contentPacks })` is\n *\n * @beta\n */\nexport function readPacks(files: readonly string[]): PackMod[] {\n\treturn files.map((file) => {\n\t\tconst text = readFileSync(file, 'utf8')\n\t\tconst pack = packFromJson(text, file)\n\t\tconst wrong = checkPackSchema(JSON.parse(text), packSchema)\n\t\tif (wrong) throw new PackError(`${file}: ${wrong}`)\n\t\treturn pack\n\t})\n}\n\n/**\n * A server's game with its packs of content composed in (`readPacks`, `composeGame`): the game as is with none\n *\n * @beta\n */\nexport function mergePackFiles(game: Rules, files: readonly string[]): Rules {\n\treturn files.length ? composeGame(game, readPacks(files)) : game\n}\n"],"mappings":";;;;;;;;;;;AAmBA,SAAgB,UAAU,OAAqC;CAC9D,OAAO,MAAM,KAAK,SAAS;EAC1B,MAAM,OAAO,aAAa,MAAM,MAAM;EACtC,MAAM,OAAO,aAAa,MAAM,IAAI;EACpC,MAAM,QAAQ,gBAAgB,KAAK,MAAM,IAAI,GAAGA,mBAAU;EAC1D,IAAI,OAAO,MAAM,IAAI,UAAU,GAAG,KAAK,IAAI,OAAO;EAClD,OAAO;CACR,CAAC;AACF;;;;;;AAOA,SAAgB,eAAe,MAAa,OAAiC;CAC5E,OAAO,MAAM,SAAS,YAAY,MAAM,UAAU,KAAK,CAAC,IAAI;AAC7D"}
|
package/dist/server/ratings.d.ts
CHANGED
|
@@ -5,8 +5,11 @@ import type { GameResult } from '#engine/game';
|
|
|
5
5
|
* @beta
|
|
6
6
|
*/
|
|
7
7
|
export interface Rating {
|
|
8
|
+
/** The player's rating number in the mode, moved by every rated game they finish */
|
|
8
9
|
rating: number;
|
|
10
|
+
/** How many rated games they have played in the mode */
|
|
9
11
|
games: number;
|
|
12
|
+
/** How many of those games they won */
|
|
10
13
|
wins: number;
|
|
11
14
|
/** The name they last played by */
|
|
12
15
|
name: string;
|
|
@@ -37,7 +40,9 @@ export interface Ratings {
|
|
|
37
40
|
* @beta
|
|
38
41
|
*/
|
|
39
42
|
export interface RatedPlayer extends Rating {
|
|
43
|
+
/** The player's rated id (`ratedId`), a hash of who they are, never their key */
|
|
40
44
|
id: string;
|
|
45
|
+
/** Their place among the best, counted from 1 */
|
|
41
46
|
place: number;
|
|
42
47
|
}
|
|
43
48
|
/**
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
//#region src/server/ratings.ts
|
|
5
|
+
/**
|
|
6
|
+
* The id a player is rated by: a hash of their key or login id, safe to show and keep. The same key, the same id
|
|
7
|
+
*
|
|
8
|
+
* @beta
|
|
9
|
+
*/
|
|
10
|
+
var ratedId = (key) => createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
11
|
+
/**
|
|
12
|
+
* Ratings kept in a file (`file`, JSON, read once and written after every game) or in memory: Elo, each side's
|
|
13
|
+
* players against the average of the other's, `k` 32, starting at 1500. The computer counts as `start`, and is not
|
|
14
|
+
* rated; a game with no winner, or with players of one side only, changes nothing
|
|
15
|
+
*
|
|
16
|
+
* @beta
|
|
17
|
+
*/
|
|
18
|
+
function createRatings(options = {}) {
|
|
19
|
+
const k = options.k ?? 32;
|
|
20
|
+
const start = options.start ?? 1500;
|
|
21
|
+
let byMode = {};
|
|
22
|
+
const ready = (async () => {
|
|
23
|
+
if (!options.file) return;
|
|
24
|
+
let text;
|
|
25
|
+
try {
|
|
26
|
+
text = await readFile(options.file, "utf8");
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.code === "ENOENT") return;
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
byMode = JSON.parse(text);
|
|
32
|
+
})();
|
|
33
|
+
ready.catch(() => void 0);
|
|
34
|
+
let writing = Promise.resolve();
|
|
35
|
+
const keep = () => {
|
|
36
|
+
if (!options.file) return writing;
|
|
37
|
+
const file = options.file;
|
|
38
|
+
writing = writing.then(async () => {
|
|
39
|
+
await mkdir(dirname(file), { recursive: true });
|
|
40
|
+
await writeFile(file, JSON.stringify(byMode, null, 1));
|
|
41
|
+
});
|
|
42
|
+
return writing;
|
|
43
|
+
};
|
|
44
|
+
/** A mode's players, best first: by rating, then by more games, then by id, so the order is always the same */
|
|
45
|
+
const ranked = (mode) => Object.entries(byMode[mode] ?? {}).sort(([a, x], [b, y]) => y.rating - x.rating || y.games - x.games || (a < b ? -1 : 1)).map(([id, rating], index) => ({
|
|
46
|
+
id,
|
|
47
|
+
place: index + 1,
|
|
48
|
+
...rating
|
|
49
|
+
}));
|
|
50
|
+
return {
|
|
51
|
+
ready,
|
|
52
|
+
get: (mode, player) => byMode[mode]?.[player],
|
|
53
|
+
top: (mode, count) => ranked(mode).slice(0, Math.max(0, count)),
|
|
54
|
+
placeOf: (mode, player) => ranked(mode).find((each) => each.id === player),
|
|
55
|
+
async record(result) {
|
|
56
|
+
await ready;
|
|
57
|
+
if (result.winner === null || result.failed) return;
|
|
58
|
+
const seats = result.seats.filter((seat) => seat.outcome !== "none");
|
|
59
|
+
const rated = seats.filter((seat) => !seat.ai && seat.rated);
|
|
60
|
+
if (!rated.length) return;
|
|
61
|
+
const table = byMode[result.mode] ??= {};
|
|
62
|
+
const ratingOf = (seat) => !seat.ai && seat.rated ? table[seat.rated]?.rating ?? start : start;
|
|
63
|
+
const sides = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const seat of seats) {
|
|
65
|
+
const side = seat.outcome;
|
|
66
|
+
sides.set(side, [...sides.get(side) ?? [], seat]);
|
|
67
|
+
}
|
|
68
|
+
const winners = sides.get("victory") ?? [];
|
|
69
|
+
const losers = sides.get("defeat") ?? [];
|
|
70
|
+
if (!winners.length || !losers.length) return;
|
|
71
|
+
const average = (list) => list.reduce((sum, seat) => sum + ratingOf(seat), 0) / list.length;
|
|
72
|
+
const changes = rated.map((seat) => {
|
|
73
|
+
const won = seat.outcome === "victory";
|
|
74
|
+
const theirs = average(won ? losers : winners);
|
|
75
|
+
const mine = ratingOf(seat);
|
|
76
|
+
const expected = 1 / (1 + 10 ** ((theirs - mine) / 400));
|
|
77
|
+
return {
|
|
78
|
+
seat,
|
|
79
|
+
won,
|
|
80
|
+
next: mine + k * ((won ? 1 : 0) - expected)
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
for (const { seat, won, next } of changes) {
|
|
84
|
+
const before = table[seat.rated] ?? {
|
|
85
|
+
rating: start,
|
|
86
|
+
games: 0,
|
|
87
|
+
wins: 0,
|
|
88
|
+
name: seat.name
|
|
89
|
+
};
|
|
90
|
+
table[seat.rated] = {
|
|
91
|
+
rating: Math.round(next),
|
|
92
|
+
games: before.games + 1,
|
|
93
|
+
wins: before.wins + (won ? 1 : 0),
|
|
94
|
+
name: seat.name
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
await keep();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { createRatings, ratedId };
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=ratings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratings.js","names":[],"sources":["../../src/server/ratings.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport type { GameResult } from '#engine/game'\n\n/*\n * Ranks (features-plan H4): a rating for each player in each mode, Elo, kept by the server from the results of its\n * games. There are no accounts: a player is who brings the same key (`join`'s `who`, kept in their browser), or who a\n * login says they are (`SeatAnswer.id`); what is kept and shown is a hash of it (`ratedId`), never the key\n */\n\n/**\n * A player's standing in a mode\n *\n * @beta\n */\nexport interface Rating {\n\t/** The player's rating number in the mode, moved by every rated game they finish */\n\trating: number\n\t/** How many rated games they have played in the mode */\n\tgames: number\n\t/** How many of those games they won */\n\twins: number\n\t/** The name they last played by */\n\tname: string\n}\n\n/**\n * Where a server keeps ratings (`GameServerOptions.ratings`): what it reads to show in the lobby and the list, and\n * what it tells of every game that ended. `createRatings` keeps them in a file, or in memory; a game's own keeps them\n * where it likes (a database)\n *\n * @beta\n */\nexport interface Ratings {\n\t/** A player's standing in a mode, by their rated id (`ratedId`): none for one who never played it */\n\tget(mode: string, player: string): Rating | undefined\n\t/** Takes in a game that ended: the rated seats' ratings move by how it went, against what was expected */\n\trecord(result: GameResult): void | Promise<void>\n\t/**\n\t * The best of a mode, best first, with their rated ids and places (from 1): what `/ratings` lists. A store with none\n\t * lists nothing @beta\n\t */\n\ttop?(mode: string, count: number): RatedPlayer[]\n\t/** A player's place in a mode (from 1), with their standing: none for one who never played it @beta */\n\tplaceOf?(mode: string, player: string): RatedPlayer | undefined\n}\n\n/**\n * A player in a list of the best: their standing, their rated id (`ratedId`), and their place, from 1\n *\n * @beta\n */\nexport interface RatedPlayer extends Rating {\n\t/** The player's rated id (`ratedId`), a hash of who they are, never their key */\n\tid: string\n\t/** Their place among the best, counted from 1 */\n\tplace: number\n}\n\n/**\n * The id a player is rated by: a hash of their key or login id, safe to show and keep. The same key, the same id\n *\n * @beta\n */\nexport const ratedId = (key: string): string => createHash('sha256').update(key).digest('hex').slice(0, 16)\n\n/**\n * Ratings kept in a file (`file`, JSON, read once and written after every game) or in memory: Elo, each side's\n * players against the average of the other's, `k` 32, starting at 1500. The computer counts as `start`, and is not\n * rated; a game with no winner, or with players of one side only, changes nothing\n *\n * @beta\n */\nexport function createRatings(options: { file?: string; k?: number; start?: number } = {}): Ratings & { ready: Promise<void> } {\n\tconst k = options.k ?? 32\n\tconst start = options.start ?? 1500\n\tlet byMode: Record<string, Record<string, Rating>> = {}\n\tconst ready = (async () => {\n\t\tif (!options.file) return\n\t\t// none yet: empty. One that cannot be read is not written over, nor started from empty: it says so\n\t\tlet text: string\n\t\ttry {\n\t\t\ttext = await readFile(options.file, 'utf8')\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n\t\t\tthrow error\n\t\t}\n\t\tbyMode = JSON.parse(text)\n\t})()\n\t// told to whoever waits for it (`ready`, `record`), not to the process as a rejection nobody heard\n\tready.catch(() => undefined)\n\tlet writing = Promise.resolve()\n\tconst keep = () => {\n\t\tif (!options.file) return writing\n\t\tconst file = options.file\n\t\twriting = writing.then(async () => {\n\t\t\tawait mkdir(dirname(file), { recursive: true })\n\t\t\tawait writeFile(file, JSON.stringify(byMode, null, 1))\n\t\t})\n\t\treturn writing\n\t}\n\t/** A mode's players, best first: by rating, then by more games, then by id, so the order is always the same */\n\tconst ranked = (mode: string): RatedPlayer[] =>\n\t\tObject.entries(byMode[mode] ?? {})\n\t\t\t.sort(([a, x], [b, y]) => y.rating - x.rating || y.games - x.games || (a < b ? -1 : 1))\n\t\t\t.map(([id, rating], index) => ({ id, place: index + 1, ...rating }))\n\treturn {\n\t\tready,\n\t\tget: (mode, player) => byMode[mode]?.[player],\n\t\ttop: (mode, count) => ranked(mode).slice(0, Math.max(0, count)),\n\t\tplaceOf: (mode, player) => ranked(mode).find((each) => each.id === player),\n\t\tasync record(result) {\n\t\t\tawait ready\n\t\t\tif (result.winner === null || result.failed) return\n\t\t\tconst seats = result.seats.filter((seat) => seat.outcome !== 'none')\n\t\t\tconst rated = seats.filter((seat) => !seat.ai && seat.rated)\n\t\t\tif (!rated.length) return\n\t\t\tconst table = (byMode[result.mode] ??= {})\n\t\t\tconst ratingOf = (seat: (typeof seats)[number]) => (!seat.ai && seat.rated ? (table[seat.rated]?.rating ?? start) : start)\n\t\t\tconst sides = new Map<string, (typeof seats)[number][]>()\n\t\t\tfor (const seat of seats) {\n\t\t\t\tconst side = seat.outcome\n\t\t\t\tsides.set(side, [...(sides.get(side) ?? []), seat])\n\t\t\t}\n\t\t\tconst winners = sides.get('victory') ?? []\n\t\t\tconst losers = sides.get('defeat') ?? []\n\t\t\tif (!winners.length || !losers.length) return\n\t\t\tconst average = (list: typeof seats) => list.reduce((sum, seat) => sum + ratingOf(seat), 0) / list.length\n\t\t\tconst changes = rated.map((seat) => {\n\t\t\t\tconst won = seat.outcome === 'victory'\n\t\t\t\tconst theirs = average(won ? losers : winners)\n\t\t\t\tconst mine = ratingOf(seat)\n\t\t\t\tconst expected = 1 / (1 + 10 ** ((theirs - mine) / 400))\n\t\t\t\treturn { seat, won, next: mine + k * ((won ? 1 : 0) - expected) }\n\t\t\t})\n\t\t\tfor (const { seat, won, next } of changes) {\n\t\t\t\tconst before = table[seat.rated!] ?? { rating: start, games: 0, wins: 0, name: seat.name }\n\t\t\t\ttable[seat.rated!] = { rating: Math.round(next), games: before.games + 1, wins: before.wins + (won ? 1 : 0), name: seat.name }\n\t\t\t}\n\t\t\tawait keep()\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
package/dist/server/relay.d.ts
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import type { Logger } from '#engine/log';
|
|
2
2
|
import { type ServerLog } from './log';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* What a `WatchRelay` is started with: the port, compression and the log
|
|
5
|
+
*
|
|
6
|
+
* @beta
|
|
7
|
+
*/
|
|
4
8
|
export interface RelayOptions {
|
|
5
9
|
/** Port to listen on: 0 for any free one */
|
|
6
10
|
port: number;
|
|
11
|
+
/** Whether to compress what goes over connections whose other end can take it (`permessage-deflate`); yes when left out */
|
|
7
12
|
compress?: boolean;
|
|
8
13
|
/** Where it says what it does, as `GameServerOptions.log` */
|
|
9
14
|
log?: ServerLog;
|
|
10
15
|
}
|
|
11
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* A server that relays a game from the one who shares it to the watchers who connect to it
|
|
18
|
+
*
|
|
19
|
+
* @beta
|
|
20
|
+
*/
|
|
12
21
|
export declare class WatchRelay {
|
|
13
22
|
private readonly socket;
|
|
14
23
|
private readonly shares;
|
|
@@ -19,6 +28,7 @@ export declare class WatchRelay {
|
|
|
19
28
|
private constructor();
|
|
20
29
|
/** Starts a relay, and resolves once it listens */
|
|
21
30
|
static start(options: RelayOptions): Promise<WatchRelay>;
|
|
31
|
+
/** The port the relay listens on, the real one where 0 was asked for */
|
|
22
32
|
get port(): number;
|
|
23
33
|
/** How many watch what is shared under a key */
|
|
24
34
|
watching(key: string): number;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { readMessage } from "../session/protocol.js";
|
|
2
|
+
import { serverLogger } from "./log.js";
|
|
3
|
+
import { startSocketServer } from "./websocket.js";
|
|
4
|
+
//#region src/server/relay.ts
|
|
5
|
+
/**
|
|
6
|
+
* A server that relays a game from the one who shares it to the watchers who connect to it
|
|
7
|
+
*
|
|
8
|
+
* @beta
|
|
9
|
+
*/
|
|
10
|
+
var WatchRelay = class WatchRelay {
|
|
11
|
+
socket;
|
|
12
|
+
shares = /* @__PURE__ */ new Map();
|
|
13
|
+
watchers = /* @__PURE__ */ new Map();
|
|
14
|
+
next = 1;
|
|
15
|
+
/** What the relay says: see `RelayOptions.log` */
|
|
16
|
+
logger;
|
|
17
|
+
constructor(socket, options) {
|
|
18
|
+
this.socket = socket;
|
|
19
|
+
this.logger = serverLogger(options.log);
|
|
20
|
+
socket.onConnection((connection) => this.accept(connection));
|
|
21
|
+
}
|
|
22
|
+
/** Starts a relay, and resolves once it listens */
|
|
23
|
+
static async start(options) {
|
|
24
|
+
const socket = await startSocketServer({
|
|
25
|
+
port: options.port,
|
|
26
|
+
compress: options.compress,
|
|
27
|
+
maxMessage: 16777216
|
|
28
|
+
});
|
|
29
|
+
const relay = new WatchRelay(socket, options);
|
|
30
|
+
relay.logger.info("server", `a relay for games watched, on port ${socket.port}`, { port: socket.port });
|
|
31
|
+
return relay;
|
|
32
|
+
}
|
|
33
|
+
/** The port the relay listens on, the real one where 0 was asked for */
|
|
34
|
+
get port() {
|
|
35
|
+
return this.socket.port;
|
|
36
|
+
}
|
|
37
|
+
/** How many watch what is shared under a key */
|
|
38
|
+
watching(key) {
|
|
39
|
+
return [...this.watchers.values()].filter((watcher) => watcher.key === key).length;
|
|
40
|
+
}
|
|
41
|
+
accept(connection) {
|
|
42
|
+
let role;
|
|
43
|
+
connection.onMessage((text) => {
|
|
44
|
+
if (role && "watcher" in role) return this.fromWatcher(role.watcher, text);
|
|
45
|
+
const message = readMessage(text);
|
|
46
|
+
if (!message) return;
|
|
47
|
+
if (!role && message.type === "share") {
|
|
48
|
+
if (typeof message.key === "string" && typeof message.name === "string") role = this.share(connection, {
|
|
49
|
+
type: "share",
|
|
50
|
+
key: message.key,
|
|
51
|
+
name: message.name
|
|
52
|
+
});
|
|
53
|
+
} else if (!role && message.type === "watch") {
|
|
54
|
+
if (typeof message.key === "string") role = { watcher: this.watch(connection, message.key) };
|
|
55
|
+
} else if (role && "share" in role && message.type === "send" && typeof message.to === "number" && typeof message.text === "string") {
|
|
56
|
+
const watcher = this.watchers.get(message.to);
|
|
57
|
+
if (watcher?.key === role.share.key) watcher.connection.send(message.text);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
connection.onClose(() => {
|
|
61
|
+
if (role && "share" in role) this.unshare(role.share);
|
|
62
|
+
else if (role && "watcher" in role) this.leave(role.watcher);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
tell(connection, notice) {
|
|
66
|
+
connection.send(JSON.stringify(notice));
|
|
67
|
+
}
|
|
68
|
+
share(host, message) {
|
|
69
|
+
if (this.shares.has(message.key)) {
|
|
70
|
+
this.tell(host, {
|
|
71
|
+
type: "taken",
|
|
72
|
+
key: message.key
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const share = {
|
|
77
|
+
key: message.key,
|
|
78
|
+
name: message.name.slice(0, 80),
|
|
79
|
+
host
|
|
80
|
+
};
|
|
81
|
+
this.shares.set(share.key, share);
|
|
82
|
+
this.tell(host, {
|
|
83
|
+
type: "shared",
|
|
84
|
+
key: share.key
|
|
85
|
+
});
|
|
86
|
+
this.logger.info("server", `${share.name} is shared under ${share.key}`, { name: share.name });
|
|
87
|
+
for (const watcher of this.watchers.values()) if (watcher.key === share.key) this.tell(host, {
|
|
88
|
+
type: "watcher",
|
|
89
|
+
id: watcher.id
|
|
90
|
+
});
|
|
91
|
+
return { share };
|
|
92
|
+
}
|
|
93
|
+
unshare(share) {
|
|
94
|
+
if (this.shares.get(share.key) !== share) return;
|
|
95
|
+
this.shares.delete(share.key);
|
|
96
|
+
this.logger.info("server", `${share.name} is no longer shared`, { name: share.name });
|
|
97
|
+
for (const watcher of this.watchers.values()) if (watcher.key === share.key) this.tell(watcher.connection, {
|
|
98
|
+
type: "ended",
|
|
99
|
+
key: share.key
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
watch(connection, key) {
|
|
103
|
+
const watcher = {
|
|
104
|
+
id: this.next++,
|
|
105
|
+
connection,
|
|
106
|
+
key
|
|
107
|
+
};
|
|
108
|
+
this.watchers.set(watcher.id, watcher);
|
|
109
|
+
const share = this.shares.get(key);
|
|
110
|
+
if (share) this.tell(share.host, {
|
|
111
|
+
type: "watcher",
|
|
112
|
+
id: watcher.id
|
|
113
|
+
});
|
|
114
|
+
else this.tell(connection, {
|
|
115
|
+
type: "waiting",
|
|
116
|
+
key
|
|
117
|
+
});
|
|
118
|
+
return watcher;
|
|
119
|
+
}
|
|
120
|
+
fromWatcher(watcher, text) {
|
|
121
|
+
const share = this.shares.get(watcher.key);
|
|
122
|
+
if (share) this.tell(share.host, {
|
|
123
|
+
type: "from",
|
|
124
|
+
watcher: watcher.id,
|
|
125
|
+
text
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
leave(watcher) {
|
|
129
|
+
if (!this.watchers.delete(watcher.id)) return;
|
|
130
|
+
const share = this.shares.get(watcher.key);
|
|
131
|
+
if (share) this.tell(share.host, {
|
|
132
|
+
type: "gone",
|
|
133
|
+
watcher: watcher.id
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/** Stops the relay: every connection closed */
|
|
137
|
+
async close() {
|
|
138
|
+
await this.socket.close();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
//#endregion
|
|
142
|
+
export { WatchRelay };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.js","names":[],"sources":["../../src/server/relay.ts"],"sourcesContent":["import type { Logger } from '#engine/log'\nimport { type ServerLog, serverLogger } from './log'\nimport { readMessage } from '#engine/session/protocol'\nimport type { RelayNotice, RelayRequest } from '#engine/session/share'\nimport { type Connection, type SocketServer, startSocketServer } from './websocket'\n\n/*\n * A relay for games played in a page and watched from others. A page cannot take connections, so the one that plays\n * shares its game here (`share`, under a key like the campaign's id), and those who watch ask for it by that key\n * (`watch`). The relay only passes text along (`RelayRequest`, `RelayNotice`): the playing page's host makes each\n * watcher's game, as a server does for its observers (`shareGame`), and the relay hands it to the watcher as it came,\n * and the watcher's answers back. A watcher who comes before the game is shared, or stays after it ends (a mission\n * over, the next one loading), waits for it under its key, and is let in again when it is shared anew\n */\n\ninterface Share {\n\tkey: string\n\tname: string\n\thost: Connection\n}\n\ninterface Watcher {\n\tid: number\n\tconnection: Connection\n\tkey: string\n}\n\n/**\n * What a `WatchRelay` is started with: the port, compression and the log\n *\n * @beta\n */\nexport interface RelayOptions {\n\t/** Port to listen on: 0 for any free one */\n\tport: number\n\t/** Whether to compress what goes over connections whose other end can take it (`permessage-deflate`); yes when left out */\n\tcompress?: boolean\n\t/** Where it says what it does, as `GameServerOptions.log` */\n\tlog?: ServerLog\n}\n\n/**\n * A server that relays a game from the one who shares it to the watchers who connect to it\n *\n * @beta\n */\nexport class WatchRelay {\n\tprivate readonly shares = new Map<string, Share>()\n\tprivate readonly watchers = new Map<number, Watcher>()\n\tprivate next = 1\n\n\t/** What the relay says: see `RelayOptions.log` */\n\treadonly logger: Logger\n\n\tprivate constructor(\n\t\tprivate readonly socket: SocketServer,\n\t\toptions: RelayOptions\n\t) {\n\t\tthis.logger = serverLogger(options.log)\n\t\tsocket.onConnection((connection) => this.accept(connection))\n\t}\n\n\t/** Starts a relay, and resolves once it listens */\n\tstatic async start(options: RelayOptions): Promise<WatchRelay> {\n\t\t// a whole map goes through as one message, to each watcher who comes\n\t\tconst socket = await startSocketServer({ port: options.port, compress: options.compress, maxMessage: 16 * 1024 * 1024 })\n\t\tconst relay = new WatchRelay(socket, options)\n\t\trelay.logger.info('server', `a relay for games watched, on port ${socket.port}`, { port: socket.port })\n\t\treturn relay\n\t}\n\n\t/** The port the relay listens on, the real one where 0 was asked for */\n\tget port(): number {\n\t\treturn this.socket.port\n\t}\n\n\t/** How many watch what is shared under a key */\n\twatching(key: string): number {\n\t\treturn [...this.watchers.values()].filter((watcher) => watcher.key === key).length\n\t}\n\n\tprivate accept(connection: Connection): void {\n\t\tlet role: { share: Share } | { watcher: Watcher } | undefined\n\t\tconnection.onMessage((text) => {\n\t\t\t// what a watcher says goes to the game it watches, as it is\n\t\t\tif (role && 'watcher' in role) return this.fromWatcher(role.watcher, text)\n\t\t\t// anyone can connect and send anything: what is not as it should be is dropped\n\t\t\tconst message = readMessage(text)\n\t\t\tif (!message) return\n\t\t\tif (!role && message.type === 'share') {\n\t\t\t\tif (typeof message.key === 'string' && typeof message.name === 'string')\n\t\t\t\t\trole = this.share(connection, { type: 'share', key: message.key, name: message.name })\n\t\t\t} else if (!role && message.type === 'watch') {\n\t\t\t\tif (typeof message.key === 'string') role = { watcher: this.watch(connection, message.key) }\n\t\t\t} else if (role && 'share' in role && message.type === 'send' && typeof message.to === 'number' && typeof message.text === 'string') {\n\t\t\t\t// only to who watches this share: a game shared under one key says nothing to the watchers of another\n\t\t\t\tconst watcher = this.watchers.get(message.to)\n\t\t\t\tif (watcher?.key === role.share.key) watcher.connection.send(message.text)\n\t\t\t}\n\t\t})\n\t\tconnection.onClose(() => {\n\t\t\tif (role && 'share' in role) this.unshare(role.share)\n\t\t\telse if (role && 'watcher' in role) this.leave(role.watcher)\n\t\t})\n\t}\n\n\tprivate tell(connection: Connection, notice: RelayNotice): void {\n\t\tconnection.send(JSON.stringify(notice))\n\t}\n\n\tprivate share(host: Connection, message: Extract<RelayRequest, { type: 'share' }>): { share: Share } | undefined {\n\t\tif (this.shares.has(message.key)) {\n\t\t\tthis.tell(host, { type: 'taken', key: message.key })\n\t\t\treturn undefined\n\t\t}\n\t\tconst share: Share = { key: message.key, name: message.name.slice(0, 80), host }\n\t\tthis.shares.set(share.key, share)\n\t\tthis.tell(host, { type: 'shared', key: share.key })\n\t\tthis.logger.info('server', `${share.name} is shared under ${share.key}`, { name: share.name })\n\t\t// those who waited for it\n\t\tfor (const watcher of this.watchers.values()) if (watcher.key === share.key) this.tell(host, { type: 'watcher', id: watcher.id })\n\t\treturn { share }\n\t}\n\n\tprivate unshare(share: Share): void {\n\t\tif (this.shares.get(share.key) !== share) return\n\t\tthis.shares.delete(share.key)\n\t\tthis.logger.info('server', `${share.name} is no longer shared`, { name: share.name })\n\t\tfor (const watcher of this.watchers.values()) if (watcher.key === share.key) this.tell(watcher.connection, { type: 'ended', key: share.key })\n\t}\n\n\tprivate watch(connection: Connection, key: string): Watcher {\n\t\tconst watcher: Watcher = { id: this.next++, connection, key }\n\t\tthis.watchers.set(watcher.id, watcher)\n\t\tconst share = this.shares.get(key)\n\t\tif (share) this.tell(share.host, { type: 'watcher', id: watcher.id })\n\t\telse this.tell(connection, { type: 'waiting', key })\n\t\treturn watcher\n\t}\n\n\tprivate fromWatcher(watcher: Watcher, text: string): void {\n\t\tconst share = this.shares.get(watcher.key)\n\t\tif (share) this.tell(share.host, { type: 'from', watcher: watcher.id, text })\n\t}\n\n\tprivate leave(watcher: Watcher): void {\n\t\tif (!this.watchers.delete(watcher.id)) return\n\t\tconst share = this.shares.get(watcher.key)\n\t\tif (share) this.tell(share.host, { type: 'gone', watcher: watcher.id })\n\t}\n\n\t/** Stops the relay: every connection closed */\n\tasync close(): Promise<void> {\n\t\tawait this.socket.close()\n\t}\n}\n"],"mappings":"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"}
|