@v12-engine/engine 0.1.1 → 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
package/dist/server/rooms.d.ts
CHANGED
|
@@ -24,7 +24,11 @@ export interface RoomKind {
|
|
|
24
24
|
*/
|
|
25
25
|
room: RoomOptions | ((settings: Record<string, string>) => RoomOptions);
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* What `GameRooms` is started with: the shared port, the rooms, the kinds players may make and matchmaking
|
|
29
|
+
*
|
|
30
|
+
* @beta
|
|
31
|
+
*/
|
|
28
32
|
export interface GameRoomsOptions {
|
|
29
33
|
/** The port they share: 8787 when left out, 0 for any free one */
|
|
30
34
|
port?: number;
|
|
@@ -51,8 +55,11 @@ export interface GameRoomsOptions {
|
|
|
51
55
|
matchmaking?: boolean | {
|
|
52
56
|
path?: string;
|
|
53
57
|
};
|
|
58
|
+
/** Whether to compress what goes over connections whose other end can take it; as `SocketServerOptions.compress` */
|
|
54
59
|
compress?: boolean;
|
|
60
|
+
/** Seconds a connection may be silent before it is closed; as `SocketServerOptions.idleTimeout` */
|
|
55
61
|
idleTimeout?: number;
|
|
62
|
+
/** Bytes that may wait unsent on a connection before it is closed; as `SocketServerOptions.maxBuffered` */
|
|
56
63
|
maxBuffered?: number;
|
|
57
64
|
/**
|
|
58
65
|
* Connections open at once to all the rooms and the queue, at most (`SocketServerOptions.maxConnections`). 1024 when
|
|
@@ -82,7 +89,7 @@ export interface GameRoomsOptions {
|
|
|
82
89
|
* and closed. Rooms are added in code (`add`), or made by players of the kinds it offers (`kinds`, `POST /games`) and
|
|
83
90
|
* closed once empty
|
|
84
91
|
*
|
|
85
|
-
* @
|
|
92
|
+
* @public
|
|
86
93
|
*/
|
|
87
94
|
export declare class GameRooms {
|
|
88
95
|
private readonly socket;
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { serverLogger } from "./log.js";
|
|
2
|
+
import { answerOps } from "./ops.js";
|
|
3
|
+
import { startSocketServer } from "./websocket.js";
|
|
4
|
+
import { GameServer } from "./gameServer.js";
|
|
5
|
+
//#region src/server/rooms.ts
|
|
6
|
+
/** What a room is reached at: the first part of the address a connection asked for */
|
|
7
|
+
var roomOf = (url) => new URL(url, "ws://server").pathname.split("/")[1] ?? "";
|
|
8
|
+
/** A name for a room: letters, digits, `-` and `_` */
|
|
9
|
+
var isRoomName = (name) => /^[\w-]+$/.test(name);
|
|
10
|
+
/** What an address that asked for games too often is told */
|
|
11
|
+
var TOO_OFTEN = "too many games asked for: wait a minute";
|
|
12
|
+
/** The most a body of a request to make a room may be: a kind and a few settings */
|
|
13
|
+
var MAX_BODY = 4096;
|
|
14
|
+
/**
|
|
15
|
+
* Where a room's game server gets its connections (`ConnectionSource`): those the shared port hands it for the room's
|
|
16
|
+
* name. Its server is told of a connection closing after the room has let go of it, and its `close` closes those in
|
|
17
|
+
* the room, never the port
|
|
18
|
+
*/
|
|
19
|
+
var RoomConnections = class {
|
|
20
|
+
port;
|
|
21
|
+
handler = () => {};
|
|
22
|
+
open = /* @__PURE__ */ new Set();
|
|
23
|
+
constructor(port) {
|
|
24
|
+
this.port = port;
|
|
25
|
+
}
|
|
26
|
+
onConnection(handler) {
|
|
27
|
+
this.handler = handler;
|
|
28
|
+
}
|
|
29
|
+
/** A connection to the room, handed to its server */
|
|
30
|
+
hand(connection) {
|
|
31
|
+
let closed = () => {};
|
|
32
|
+
const theirs = {
|
|
33
|
+
get url() {
|
|
34
|
+
return connection.url;
|
|
35
|
+
},
|
|
36
|
+
get address() {
|
|
37
|
+
return connection.address;
|
|
38
|
+
},
|
|
39
|
+
get bytesSent() {
|
|
40
|
+
return connection.bytesSent;
|
|
41
|
+
},
|
|
42
|
+
get compressed() {
|
|
43
|
+
return connection.compressed;
|
|
44
|
+
},
|
|
45
|
+
send: (message) => connection.send(message),
|
|
46
|
+
onMessage: (next) => connection.onMessage(next),
|
|
47
|
+
onClose: (next) => void (closed = next),
|
|
48
|
+
close: () => connection.close()
|
|
49
|
+
};
|
|
50
|
+
this.open.add(theirs);
|
|
51
|
+
connection.onClose(() => {
|
|
52
|
+
this.open.delete(theirs);
|
|
53
|
+
closed();
|
|
54
|
+
});
|
|
55
|
+
this.handler(theirs);
|
|
56
|
+
}
|
|
57
|
+
async close() {
|
|
58
|
+
for (const connection of [...this.open]) connection.close();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Many games in a process (S58): game servers that share a port, each a room reached at its name, as a server of its
|
|
63
|
+
* own would be at its port. One process, one thread: each room's games step in turn, so the rooms of a process are as
|
|
64
|
+
* many as its ticks have time for (`/metrics` says how long they take). A connection to a room there is not is told so
|
|
65
|
+
* and closed. Rooms are added in code (`add`), or made by players of the kinds it offers (`kinds`, `POST /games`) and
|
|
66
|
+
* closed once empty
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
var GameRooms = class GameRooms {
|
|
71
|
+
socket;
|
|
72
|
+
options;
|
|
73
|
+
logger;
|
|
74
|
+
servers = /* @__PURE__ */ new Map();
|
|
75
|
+
/** Where each room's connections come from, by its name */
|
|
76
|
+
doors = /* @__PURE__ */ new Map();
|
|
77
|
+
/** The rooms players made, and since when each has been empty (nobody connected): what closes them */
|
|
78
|
+
made = /* @__PURE__ */ new Map();
|
|
79
|
+
/** When each address last asked for rooms (`POST /games`): a few a minute each */
|
|
80
|
+
askedBy = /* @__PURE__ */ new Map();
|
|
81
|
+
sweeper;
|
|
82
|
+
nextMade = 1;
|
|
83
|
+
/** Who waits to be matched, by kind, in the order they came */
|
|
84
|
+
queues = /* @__PURE__ */ new Map();
|
|
85
|
+
constructor(socket, options, logger) {
|
|
86
|
+
this.socket = socket;
|
|
87
|
+
this.options = options;
|
|
88
|
+
this.logger = logger;
|
|
89
|
+
}
|
|
90
|
+
/** The rooms there are now, by name */
|
|
91
|
+
get rooms() {
|
|
92
|
+
return this.servers;
|
|
93
|
+
}
|
|
94
|
+
/** Starts the rooms, and resolves once they listen */
|
|
95
|
+
static async start(options) {
|
|
96
|
+
for (const name of Object.keys(options.rooms)) if (!isRoomName(name)) throw new Error(`GameRooms: "${name}" is no name for a room: letters, digits, - and _`);
|
|
97
|
+
const logger = serverLogger(options.log);
|
|
98
|
+
let rooms;
|
|
99
|
+
const socket = await startSocketServer({
|
|
100
|
+
port: options.port ?? 8787,
|
|
101
|
+
compress: options.compress,
|
|
102
|
+
idleTimeout: options.idleTimeout,
|
|
103
|
+
maxBuffered: options.maxBuffered,
|
|
104
|
+
maxConnections: options.maxConnections,
|
|
105
|
+
log: logger,
|
|
106
|
+
onRequest: (request, response) => {
|
|
107
|
+
if (!rooms) return;
|
|
108
|
+
if (rooms.answerMake(request, response)) return;
|
|
109
|
+
if (answerOps(request, response, options.http, () => ({
|
|
110
|
+
draining: rooms.draining,
|
|
111
|
+
rooms: rooms.metrics()
|
|
112
|
+
}), () => ({
|
|
113
|
+
games: rooms.listing(),
|
|
114
|
+
kinds: rooms.kinds()
|
|
115
|
+
}), {
|
|
116
|
+
ratings: options.ratings,
|
|
117
|
+
modes: rooms.modes()
|
|
118
|
+
})) return;
|
|
119
|
+
response.writeHead(426, { "content-type": "text/plain" });
|
|
120
|
+
response.end(`Game rooms: connect with a WebSocket to one of ${[...rooms.servers.keys()].join(", ")}\n`);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
rooms = new GameRooms(socket, options, logger);
|
|
124
|
+
socket.onConnection((connection) => rooms.connect(connection));
|
|
125
|
+
try {
|
|
126
|
+
for (const [name, room] of Object.entries(options.rooms)) await rooms.add(name, room);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
await rooms.close();
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
if (options.kinds && Object.keys(options.kinds).length) rooms.sweeper = setInterval(() => void rooms.sweep(), 1e3);
|
|
132
|
+
return rooms;
|
|
133
|
+
}
|
|
134
|
+
/** A kind it offers, by id: its own, never what every object has (`constructor`), whatever a page sends */
|
|
135
|
+
kind(id) {
|
|
136
|
+
const { kinds } = this.options;
|
|
137
|
+
return kinds && Object.hasOwn(kinds, id) ? kinds[id] : void 0;
|
|
138
|
+
}
|
|
139
|
+
/** The address of the queue, or none where there is no matchmaking */
|
|
140
|
+
get queuePath() {
|
|
141
|
+
const { matchmaking, kinds } = this.options;
|
|
142
|
+
if (matchmaking === false || !kinds || !Object.keys(kinds).length) return void 0;
|
|
143
|
+
return typeof matchmaking === "object" && matchmaking.path || "queue";
|
|
144
|
+
}
|
|
145
|
+
/** A connection to a room: handed to its game server, or told there is no such room. The queue's, to the queue */
|
|
146
|
+
connect(connection) {
|
|
147
|
+
const name = roomOf(connection.url);
|
|
148
|
+
if (name === this.queuePath) return this.waitInQueue(connection);
|
|
149
|
+
const door = this.doors.get(name);
|
|
150
|
+
if (!door) {
|
|
151
|
+
connection.send(JSON.stringify({
|
|
152
|
+
type: "refused",
|
|
153
|
+
reason: `there is no room "${name}"`
|
|
154
|
+
}));
|
|
155
|
+
return connection.close();
|
|
156
|
+
}
|
|
157
|
+
door.hand(connection);
|
|
158
|
+
}
|
|
159
|
+
/** Opens a room at a name, as `rooms` does: in code, while the rooms run */
|
|
160
|
+
async add(name, room) {
|
|
161
|
+
if (!isRoomName(name)) throw new Error(`GameRooms: "${name}" is no name for a room: letters, digits, - and _`);
|
|
162
|
+
if (this.servers.has(name)) throw new Error(`GameRooms: there is a room "${name}" already`);
|
|
163
|
+
const door = new RoomConnections(this.socket.port);
|
|
164
|
+
this.doors.set(name, door);
|
|
165
|
+
const server = await GameServer.attach(door, {
|
|
166
|
+
...room,
|
|
167
|
+
ratings: room.ratings ?? this.options.ratings,
|
|
168
|
+
log: this.logger.child({ context: () => ({ fields: { room: name } }) })
|
|
169
|
+
});
|
|
170
|
+
this.servers.set(name, server);
|
|
171
|
+
return server;
|
|
172
|
+
}
|
|
173
|
+
/** Closes a room and lets go of its name: those in it are let go */
|
|
174
|
+
async remove(name) {
|
|
175
|
+
const server = this.servers.get(name);
|
|
176
|
+
if (!server) return;
|
|
177
|
+
this.servers.delete(name);
|
|
178
|
+
this.made.delete(name);
|
|
179
|
+
await server.close();
|
|
180
|
+
this.doors.delete(name);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Makes a room of a kind it offers (`kinds`), with the settings asked for: what `POST /games` does. Its name is the
|
|
184
|
+
* kind's and a number. Refused (with why) for a kind it does not offer, too many made, or while it stops
|
|
185
|
+
*/
|
|
186
|
+
async make(kind, settings = {}, seat) {
|
|
187
|
+
const offered = this.kind(kind);
|
|
188
|
+
if (!offered) return { refused: `there is no kind of game "${kind}"` };
|
|
189
|
+
if (this.draining) return { refused: "the server is stopping: it takes no new games" };
|
|
190
|
+
if (this.made.size >= (this.options.maxMade ?? 16)) return { refused: "there are as many games as there may be: join one" };
|
|
191
|
+
let name = `${kind}-${this.nextMade++}`;
|
|
192
|
+
while (this.servers.has(name)) name = `${kind}-${this.nextMade++}`;
|
|
193
|
+
this.made.set(name, {
|
|
194
|
+
kind,
|
|
195
|
+
emptySince: Date.now(),
|
|
196
|
+
joined: false,
|
|
197
|
+
matched: !!seat
|
|
198
|
+
});
|
|
199
|
+
try {
|
|
200
|
+
const room = typeof offered.room === "function" ? offered.room(settings) : offered.room;
|
|
201
|
+
await this.add(name, {
|
|
202
|
+
...room,
|
|
203
|
+
settings: {
|
|
204
|
+
...room.settings,
|
|
205
|
+
...settings
|
|
206
|
+
},
|
|
207
|
+
...seat && { seat }
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
this.made.delete(name);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
this.logger.info("server", `made the room ${name} (${offered.name})`);
|
|
214
|
+
return { room: name };
|
|
215
|
+
}
|
|
216
|
+
/** A room a kind makes, as its listing and matchmaking read it: the one it makes, or one it made once (a kind of a function) */
|
|
217
|
+
samples = /* @__PURE__ */ new Map();
|
|
218
|
+
sampleOf(kind) {
|
|
219
|
+
const { room } = this.kind(kind);
|
|
220
|
+
if (typeof room !== "function") return room;
|
|
221
|
+
let sample = this.samples.get(kind);
|
|
222
|
+
if (!sample) this.samples.set(kind, sample = room({}));
|
|
223
|
+
return sample;
|
|
224
|
+
}
|
|
225
|
+
/** How many of a kind's seats are for players: those the computer does not play */
|
|
226
|
+
seatsFor(kind) {
|
|
227
|
+
const room = this.sampleOf(kind);
|
|
228
|
+
return room.mode.seats.filter((seat) => !room.ai?.includes(seat.player)).length;
|
|
229
|
+
}
|
|
230
|
+
/** A connection to the queue: it says the kind it waits for, is told how many wait, and its ticket once matched */
|
|
231
|
+
waitInQueue(connection) {
|
|
232
|
+
let waitingFor;
|
|
233
|
+
const leave = () => {
|
|
234
|
+
if (!waitingFor) return;
|
|
235
|
+
const queue = this.queues.get(waitingFor) ?? [];
|
|
236
|
+
const at = queue.findIndex((each) => each.connection === connection);
|
|
237
|
+
if (at >= 0) queue.splice(at, 1);
|
|
238
|
+
this.tellQueue(waitingFor);
|
|
239
|
+
waitingFor = void 0;
|
|
240
|
+
};
|
|
241
|
+
const within = this.options.joinWithin ?? 10;
|
|
242
|
+
const deadline = Number.isFinite(within) ? setTimeout(() => !waitingFor && connection.close(), Math.max(0, within) * 1e3) : void 0;
|
|
243
|
+
deadline?.unref?.();
|
|
244
|
+
connection.onClose(() => {
|
|
245
|
+
clearTimeout(deadline);
|
|
246
|
+
leave();
|
|
247
|
+
});
|
|
248
|
+
connection.onMessage((text) => {
|
|
249
|
+
let message;
|
|
250
|
+
try {
|
|
251
|
+
message = JSON.parse(text);
|
|
252
|
+
} catch {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (message.type === "leave") return leave();
|
|
256
|
+
if (message.type !== "queue") return;
|
|
257
|
+
const kind = String(message.kind ?? "");
|
|
258
|
+
if (!this.kind(kind)) return connection.send(JSON.stringify({
|
|
259
|
+
type: "refused",
|
|
260
|
+
reason: `there is no kind of game "${kind}"`
|
|
261
|
+
}));
|
|
262
|
+
if (this.askedTooOften(connection.address)) return connection.send(JSON.stringify({
|
|
263
|
+
type: "refused",
|
|
264
|
+
reason: TOO_OFTEN
|
|
265
|
+
}));
|
|
266
|
+
clearTimeout(deadline);
|
|
267
|
+
leave();
|
|
268
|
+
waitingFor = kind;
|
|
269
|
+
const queue = this.queues.get(kind) ?? [];
|
|
270
|
+
this.queues.set(kind, queue);
|
|
271
|
+
queue.push({
|
|
272
|
+
connection,
|
|
273
|
+
...typeof message.name === "string" && { name: message.name.slice(0, 24) }
|
|
274
|
+
});
|
|
275
|
+
this.match(kind).catch((error) => this.logger.error("server", "matchmaking failed", { error: String(error) }));
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Whether an address has asked for games too often to ask again now: a few a minute each, made (`POST /games`) or
|
|
280
|
+
* waited for in the queue, so that nobody fills every place there is by asking over and over. Counted when it has not
|
|
281
|
+
*/
|
|
282
|
+
askedTooOften(address) {
|
|
283
|
+
const now = Date.now();
|
|
284
|
+
const recent = (this.askedBy.get(address) ?? []).filter((at) => now - at < 6e4);
|
|
285
|
+
if (recent.length >= 3) return true;
|
|
286
|
+
recent.push(now);
|
|
287
|
+
this.askedBy.set(address, recent);
|
|
288
|
+
if (this.askedBy.size > 1024) {
|
|
289
|
+
for (const [each, times] of this.askedBy) if (times.every((at) => now - at >= 6e4)) this.askedBy.delete(each);
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
/** Tells everyone waiting for a kind how many wait, of how many it takes */
|
|
294
|
+
tellQueue(kind) {
|
|
295
|
+
const queue = this.queues.get(kind) ?? [];
|
|
296
|
+
const needed = this.seatsFor(kind);
|
|
297
|
+
for (const each of queue) each.connection.send(JSON.stringify({
|
|
298
|
+
type: "queued",
|
|
299
|
+
kind,
|
|
300
|
+
waiting: queue.length,
|
|
301
|
+
needed
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
/** As many wait for a kind as its seats: a room of it for them, which lets in only them, and a ticket each */
|
|
305
|
+
async match(kind) {
|
|
306
|
+
const queue = this.queues.get(kind);
|
|
307
|
+
const needed = this.seatsFor(kind);
|
|
308
|
+
if (queue.length < needed) return this.tellQueue(kind);
|
|
309
|
+
const players = queue.splice(0, needed);
|
|
310
|
+
const room = this.sampleOf(kind);
|
|
311
|
+
const seats = room.mode.seats.filter((seat) => !room.ai?.includes(seat.player));
|
|
312
|
+
const tickets = new Map(players.map((player, i) => [crypto.randomUUID(), {
|
|
313
|
+
player: seats[i].player,
|
|
314
|
+
name: player.name
|
|
315
|
+
}]));
|
|
316
|
+
const made = await this.make(kind, {}, (request) => {
|
|
317
|
+
const ticket = request.ticket ? tickets.get(request.ticket) : void 0;
|
|
318
|
+
return ticket ? {
|
|
319
|
+
player: ticket.player,
|
|
320
|
+
name: ticket.name,
|
|
321
|
+
id: request.ticket
|
|
322
|
+
} : { refuse: "this game is for the players matched to it" };
|
|
323
|
+
}).catch((error) => ({ refused: error instanceof Error ? error.message : String(error) }));
|
|
324
|
+
const given = [...tickets.keys()];
|
|
325
|
+
players.forEach(({ connection }, i) => connection.send(JSON.stringify("room" in made ? {
|
|
326
|
+
type: "matched",
|
|
327
|
+
kind,
|
|
328
|
+
room: made.room,
|
|
329
|
+
ticket: given[i]
|
|
330
|
+
} : {
|
|
331
|
+
type: "refused",
|
|
332
|
+
reason: made.refused
|
|
333
|
+
})));
|
|
334
|
+
this.tellQueue(kind);
|
|
335
|
+
}
|
|
336
|
+
/** Closes the rooms players made that have been empty long enough */
|
|
337
|
+
async sweep() {
|
|
338
|
+
const now = Date.now();
|
|
339
|
+
const emptyFor = (this.options.emptyFor ?? 60) * 1e3;
|
|
340
|
+
for (const [name, made] of [...this.made]) {
|
|
341
|
+
const server = this.servers.get(name);
|
|
342
|
+
if (!server) continue;
|
|
343
|
+
if (server.connections > 0) {
|
|
344
|
+
made.emptySince = now;
|
|
345
|
+
made.joined = true;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
const wait = !made.joined ? Math.min(emptyFor, 2e4) : server.phase === "game" ? Math.max(emptyFor, (server.options.abandonAfter ?? 60) * 1e3) : emptyFor;
|
|
349
|
+
if (now - made.emptySince >= wait) await this.remove(name);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/** `POST /games`: a room made of a kind it offers, `{ kind, settings }`, answered with its name. And what a page asks first */
|
|
353
|
+
answerMake(request, response) {
|
|
354
|
+
const list = this.options.http?.games ?? "/games";
|
|
355
|
+
const { pathname } = new URL(request.url ?? "/", "http://server");
|
|
356
|
+
if (!list || pathname !== list || request.method !== "POST" && request.method !== "OPTIONS") return false;
|
|
357
|
+
const cors = {
|
|
358
|
+
"access-control-allow-origin": "*",
|
|
359
|
+
"access-control-allow-methods": "GET, POST",
|
|
360
|
+
"access-control-allow-headers": "content-type"
|
|
361
|
+
};
|
|
362
|
+
if (request.method === "OPTIONS") {
|
|
363
|
+
response.writeHead(204, cors);
|
|
364
|
+
response.end();
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
const answer = (status, body) => {
|
|
368
|
+
response.writeHead(status, {
|
|
369
|
+
"content-type": "application/json",
|
|
370
|
+
"cache-control": "no-store",
|
|
371
|
+
...cors
|
|
372
|
+
});
|
|
373
|
+
response.end(JSON.stringify(body));
|
|
374
|
+
};
|
|
375
|
+
if (this.askedTooOften(request.socket.remoteAddress ?? "")) {
|
|
376
|
+
answer(429, { refused: TOO_OFTEN });
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
let body = "";
|
|
380
|
+
request.on("data", (chunk) => {
|
|
381
|
+
body += chunk.toString();
|
|
382
|
+
if (body.length > MAX_BODY) request.destroy();
|
|
383
|
+
});
|
|
384
|
+
request.on("end", () => {
|
|
385
|
+
let asked;
|
|
386
|
+
try {
|
|
387
|
+
asked = JSON.parse(body || "{}");
|
|
388
|
+
} catch {
|
|
389
|
+
return answer(400, { refused: "what was asked is not JSON" });
|
|
390
|
+
}
|
|
391
|
+
const settings = asked.settings && typeof asked.settings === "object" ? Object.fromEntries(Object.entries(asked.settings).filter(([, value]) => typeof value === "string")) : {};
|
|
392
|
+
this.make(String(asked.kind ?? ""), settings).then((made) => answer("room" in made ? 201 : 409, made), (error) => answer(500, { refused: error instanceof Error ? error.message : String(error) }));
|
|
393
|
+
});
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
/** The port they share */
|
|
397
|
+
get port() {
|
|
398
|
+
return this.socket.port;
|
|
399
|
+
}
|
|
400
|
+
/** Whether they take no new games: stopping */
|
|
401
|
+
get draining() {
|
|
402
|
+
return [...this.servers.values()].some((room) => room.draining);
|
|
403
|
+
}
|
|
404
|
+
/** The modes the rooms play, each once, in their order: what the ratings are kept by (`/ratings`) */
|
|
405
|
+
modes() {
|
|
406
|
+
return [...new Set([...this.servers.values()].map((room) => room.options.mode.id))];
|
|
407
|
+
}
|
|
408
|
+
/** Every room's game, as a list of games says it (`GET /games`) */
|
|
409
|
+
listing() {
|
|
410
|
+
return [...this.servers].map(([name, room]) => room.listing(name));
|
|
411
|
+
}
|
|
412
|
+
/** The kinds of game players may make a room of (`kinds`), by id, with their names and the settings they offer */
|
|
413
|
+
kinds() {
|
|
414
|
+
const queue = this.queuePath;
|
|
415
|
+
return Object.entries(this.options.kinds ?? {}).map(([id, kind]) => ({
|
|
416
|
+
id,
|
|
417
|
+
name: kind.name,
|
|
418
|
+
settings: { ...this.sampleOf(id).mode.rules.server?.settings },
|
|
419
|
+
...queue && {
|
|
420
|
+
queue,
|
|
421
|
+
waiting: this.queues.get(id)?.length ?? 0,
|
|
422
|
+
needed: this.seatsFor(id)
|
|
423
|
+
}
|
|
424
|
+
}));
|
|
425
|
+
}
|
|
426
|
+
/** The numbers of each room, by its name */
|
|
427
|
+
metrics() {
|
|
428
|
+
return new Map([...this.servers].map(([name, room]) => [name, room.metrics()]));
|
|
429
|
+
}
|
|
430
|
+
/** Stops every room (see `GameServer.close`: with `drain`, each waits for its game, all at once), then the port */
|
|
431
|
+
async close(options = {}) {
|
|
432
|
+
if (this.sweeper) clearInterval(this.sweeper);
|
|
433
|
+
for (const queue of this.queues.values()) for (const { connection } of queue) connection.close();
|
|
434
|
+
this.queues.clear();
|
|
435
|
+
await Promise.all([...this.servers.values()].map((room) => room.close(options)));
|
|
436
|
+
await this.socket.close();
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
//#endregion
|
|
440
|
+
export { GameRooms };
|
|
441
|
+
|
|
442
|
+
//# sourceMappingURL=rooms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rooms.js","names":[],"sources":["../../src/server/rooms.ts"],"sourcesContent":["import type { GameKindListing, GameListing } from '#engine/session/protocol'\nimport { GameServer, type GameServerOptions } from './gameServer'\nimport type { Logger } from '#engine/log'\nimport { type ServerLog, serverLogger } from './log'\nimport { answerOps, type ServerHttpOptions, type ServerMetrics } from './ops'\nimport { type Connection, type ConnectionSource, type SocketServer, startSocketServer } from './websocket'\n\n/**\n * A room's game server: all of `GameServerOptions` but what the port they share says once for all\n *\n * @beta\n */\nexport type RoomOptions = Omit<GameServerOptions, 'port' | 'compress' | 'idleTimeout' | 'maxBuffered' | 'maxConnections' | 'http' | 'log'>\n\n/**\n * A kind of game a player may make a room of (features-plan H2): what a \"New game\" makes (`POST /games`), with the\n * settings asked for (the map) where the game offers them\n *\n * @beta\n */\nexport interface RoomKind {\n\t/** What a list calls it */\n\tname: string\n\t/**\n\t * The room it makes: the same options for each, or a function that makes a room's options each time one is made,\n\t * with the settings asked for (a random map for each: `generatedMode`). A kind of a function is listed and matched\n\t * by a room it makes once, at first\n\t */\n\troom: RoomOptions | ((settings: Record<string, string>) => RoomOptions)\n}\n\n/**\n * What `GameRooms` is started with: the shared port, the rooms, the kinds players may make and matchmaking\n *\n * @beta\n */\nexport interface GameRoomsOptions {\n\t/** The port they share: 8787 when left out, 0 for any free one */\n\tport?: number\n\t/**\n\t * The rooms, by name: each a game server of its own (a lobby and its games), reached at its name\n\t * (`ws://host:8787/arena`). A name is letters, digits, `-` and `_`. They are there for as long as the process is\n\t */\n\trooms: Record<string, RoomOptions>\n\t/**\n\t * What players may make rooms of, by id (`POST /games` with `{ kind, settings }`, a \"New game\"): each room made is\n\t * named after its kind and closed once nobody has been in it for `emptyFor`. None when left out\n\t */\n\tkinds?: Record<string, RoomKind>\n\t/** How many rooms players may have made at once, those matchmaking made with them: 16 when left out */\n\tmaxMade?: number\n\t/** Seconds a made room may stay empty before it is closed: 60 when left out. A room nobody came to counts from when it was made */\n\temptyFor?: number\n\t/**\n\t * Matchmaking (features-plan H3): players wait in a queue for a kind (`kinds`), at the address `queue`\n\t * (`ws://host:8787/queue`, `{ type: 'queue', kind, name }`); once as many wait as its seats for players, it makes a\n\t * room of the kind, which lets in only them, and hands each a ticket to it (`{ type: 'matched', room, ticket }`). On\n\t * where there are kinds, unless `false`; `path` for another address\n\t */\n\tmatchmaking?: boolean | { path?: string }\n\t/** Whether to compress what goes over connections whose other end can take it; as `SocketServerOptions.compress` */\n\tcompress?: boolean\n\t/** Seconds a connection may be silent before it is closed; as `SocketServerOptions.idleTimeout` */\n\tidleTimeout?: number\n\t/** Bytes that may wait unsent on a connection before it is closed; as `SocketServerOptions.maxBuffered` */\n\tmaxBuffered?: number\n\t/**\n\t * Connections open at once to all the rooms and the queue, at most (`SocketServerOptions.maxConnections`). 1024 when\n\t * left out\n\t *\n\t * @beta\n\t */\n\tmaxConnections?: number\n\t/**\n\t * Seconds a connection to the queue has to say what it waits for (`queue`) before it is closed. 10 when left out;\n\t * a room's own connections have their room's (`GameServerOptions.joinWithin`)\n\t *\n\t * @beta\n\t */\n\tjoinWithin?: number\n\t/** Where they all say what they do, each line with its room */\n\tlog?: ServerLog\n\t/** The health and numbers addresses, for all of them: the numbers of each by its room */\n\thttp?: ServerHttpOptions\n\t/** Where ratings are kept, for every room's players (`GameServerOptions.ratings`), where a room says none of its own */\n\tratings?: GameServerOptions['ratings']\n}\n\n/** What a room is reached at: the first part of the address a connection asked for */\nconst roomOf = (url: string) => new URL(url, 'ws://server').pathname.split('/')[1] ?? ''\n\n/** A name for a room: letters, digits, `-` and `_` */\nconst isRoomName = (name: string) => /^[\\w-]+$/.test(name)\n\n/** What an address that asked for games too often is told */\nconst TOO_OFTEN = 'too many games asked for: wait a minute'\n\n/** The most a body of a request to make a room may be: a kind and a few settings */\nconst MAX_BODY = 4096\n\n/**\n * Where a room's game server gets its connections (`ConnectionSource`): those the shared port hands it for the room's\n * name. Its server is told of a connection closing after the room has let go of it, and its `close` closes those in\n * the room, never the port\n */\nclass RoomConnections implements ConnectionSource {\n\tprivate handler: (connection: Connection) => void = () => {}\n\tprivate readonly open = new Set<Connection>()\n\n\tconstructor(readonly port: number) {}\n\n\tonConnection(handler: (connection: Connection) => void): void {\n\t\tthis.handler = handler\n\t}\n\n\t/** A connection to the room, handed to its server */\n\thand(connection: Connection): void {\n\t\t// what the room's server is told of its closing, after the room has let go of it\n\t\tlet closed = () => {}\n\t\tconst theirs: Connection = {\n\t\t\tget url() {\n\t\t\t\treturn connection.url\n\t\t\t},\n\t\t\tget address() {\n\t\t\t\treturn connection.address\n\t\t\t},\n\t\t\tget bytesSent() {\n\t\t\t\treturn connection.bytesSent\n\t\t\t},\n\t\t\tget compressed() {\n\t\t\t\treturn connection.compressed\n\t\t\t},\n\t\t\tsend: (message) => connection.send(message),\n\t\t\tonMessage: (next) => connection.onMessage(next),\n\t\t\tonClose: (next) => void (closed = next),\n\t\t\tclose: () => connection.close()\n\t\t}\n\t\tthis.open.add(theirs)\n\t\tconnection.onClose(() => {\n\t\t\tthis.open.delete(theirs)\n\t\t\tclosed()\n\t\t})\n\t\tthis.handler(theirs)\n\t}\n\n\tasync close(): Promise<void> {\n\t\tfor (const connection of [...this.open]) connection.close()\n\t}\n}\n\n/**\n * Many games in a process (S58): game servers that share a port, each a room reached at its name, as a server of its\n * own would be at its port. One process, one thread: each room's games step in turn, so the rooms of a process are as\n * many as its ticks have time for (`/metrics` says how long they take). A connection to a room there is not is told so\n * and closed. Rooms are added in code (`add`), or made by players of the kinds it offers (`kinds`, `POST /games`) and\n * closed once empty\n *\n * @public\n */\nexport class GameRooms {\n\tprivate readonly servers = new Map<string, GameServer>()\n\t/** Where each room's connections come from, by its name */\n\tprivate readonly doors = new Map<string, RoomConnections>()\n\t/** The rooms players made, and since when each has been empty (nobody connected): what closes them */\n\tprivate readonly made = new Map<string, { kind: string; emptySince: number; joined: boolean; matched: boolean }>()\n\t/** When each address last asked for rooms (`POST /games`): a few a minute each */\n\tprivate readonly askedBy = new Map<string, number[]>()\n\tprivate sweeper?: ReturnType<typeof setInterval>\n\tprivate nextMade = 1\n\t/** Who waits to be matched, by kind, in the order they came */\n\tprivate readonly queues = new Map<string, { connection: Connection; name?: string }[]>()\n\n\tprivate constructor(\n\t\tprivate readonly socket: SocketServer,\n\t\tprivate readonly options: GameRoomsOptions,\n\t\tprivate readonly logger: Logger\n\t) {}\n\n\t/** The rooms there are now, by name */\n\tget rooms(): ReadonlyMap<string, GameServer> {\n\t\treturn this.servers\n\t}\n\n\t/** Starts the rooms, and resolves once they listen */\n\tstatic async start(options: GameRoomsOptions): Promise<GameRooms> {\n\t\tfor (const name of Object.keys(options.rooms)) {\n\t\t\tif (!isRoomName(name)) throw new Error(`GameRooms: \"${name}\" is no name for a room: letters, digits, - and _`)\n\t\t}\n\t\tconst logger = serverLogger(options.log)\n\t\tlet rooms: GameRooms | undefined\n\t\tconst socket = await startSocketServer({\n\t\t\tport: options.port ?? 8787,\n\t\t\tcompress: options.compress,\n\t\t\tidleTimeout: options.idleTimeout,\n\t\t\tmaxBuffered: options.maxBuffered,\n\t\t\tmaxConnections: options.maxConnections,\n\t\t\tlog: logger,\n\t\t\tonRequest: (request, response) => {\n\t\t\t\tif (!rooms) return\n\t\t\t\tif (rooms.answerMake(request, response)) return\n\t\t\t\tconst answered = answerOps(\n\t\t\t\t\trequest,\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.http,\n\t\t\t\t\t() => ({ draining: rooms!.draining, rooms: rooms!.metrics() }),\n\t\t\t\t\t() => ({ games: rooms!.listing(), kinds: rooms!.kinds() }),\n\t\t\t\t\t{ ratings: options.ratings, modes: rooms.modes() }\n\t\t\t\t)\n\t\t\t\tif (answered) return\n\t\t\t\tresponse.writeHead(426, { 'content-type': 'text/plain' })\n\t\t\t\tresponse.end(`Game rooms: connect with a WebSocket to one of ${[...rooms.servers.keys()].join(', ')}\\n`)\n\t\t\t}\n\t\t})\n\t\trooms = new GameRooms(socket, options, logger)\n\t\tsocket.onConnection((connection) => rooms!.connect(connection))\n\t\ttry {\n\t\t\tfor (const [name, room] of Object.entries(options.rooms)) await rooms.add(name, room)\n\t\t} catch (error) {\n\t\t\tawait rooms.close()\n\t\t\tthrow error\n\t\t}\n\t\t// the rooms players made, closed once empty a while\n\t\tif (options.kinds && Object.keys(options.kinds).length) rooms.sweeper = setInterval(() => void rooms!.sweep(), 1000)\n\t\treturn rooms\n\t}\n\n\t/** A kind it offers, by id: its own, never what every object has (`constructor`), whatever a page sends */\n\tprivate kind(id: string): RoomKind | undefined {\n\t\tconst { kinds } = this.options\n\t\treturn kinds && Object.hasOwn(kinds, id) ? kinds[id] : undefined\n\t}\n\n\t/** The address of the queue, or none where there is no matchmaking */\n\tprivate get queuePath(): string | undefined {\n\t\tconst { matchmaking, kinds } = this.options\n\t\tif (matchmaking === false || !kinds || !Object.keys(kinds).length) return undefined\n\t\treturn (typeof matchmaking === 'object' && matchmaking.path) || 'queue'\n\t}\n\n\t/** A connection to a room: handed to its game server, or told there is no such room. The queue's, to the queue */\n\tprivate connect(connection: Connection): void {\n\t\tconst name = roomOf(connection.url)\n\t\tif (name === this.queuePath) return this.waitInQueue(connection)\n\t\tconst door = this.doors.get(name)\n\t\tif (!door) {\n\t\t\tconnection.send(JSON.stringify({ type: 'refused', reason: `there is no room \"${name}\"` }))\n\t\t\treturn connection.close()\n\t\t}\n\t\tdoor.hand(connection)\n\t}\n\n\t/** Opens a room at a name, as `rooms` does: in code, while the rooms run */\n\tasync add(name: string, room: RoomOptions): Promise<GameServer> {\n\t\tif (!isRoomName(name)) throw new Error(`GameRooms: \"${name}\" is no name for a room: letters, digits, - and _`)\n\t\tif (this.servers.has(name)) throw new Error(`GameRooms: there is a room \"${name}\" already`)\n\t\t// what comes to its name\n\t\tconst door = new RoomConnections(this.socket.port)\n\t\tthis.doors.set(name, door)\n\t\tconst server = await GameServer.attach(door, {\n\t\t\t...room,\n\t\t\tratings: room.ratings ?? this.options.ratings,\n\t\t\tlog: this.logger.child({ context: () => ({ fields: { room: name } }) })\n\t\t})\n\t\tthis.servers.set(name, server)\n\t\treturn server\n\t}\n\n\t/** Closes a room and lets go of its name: those in it are let go */\n\tasync remove(name: string): Promise<void> {\n\t\tconst server = this.servers.get(name)\n\t\tif (!server) return\n\t\tthis.servers.delete(name)\n\t\tthis.made.delete(name)\n\t\tawait server.close()\n\t\tthis.doors.delete(name)\n\t}\n\n\t/**\n\t * Makes a room of a kind it offers (`kinds`), with the settings asked for: what `POST /games` does. Its name is the\n\t * kind's and a number. Refused (with why) for a kind it does not offer, too many made, or while it stops\n\t */\n\tasync make(kind: string, settings: Record<string, string> = {}, seat?: RoomOptions['seat']): Promise<{ room: string } | { refused: string }> {\n\t\tconst offered = this.kind(kind)\n\t\tif (!offered) return { refused: `there is no kind of game \"${kind}\"` }\n\t\tif (this.draining) return { refused: 'the server is stopping: it takes no new games' }\n\t\t// what matchmaking makes counts as well: waiting in the queue is no way past how many there may be (RV38)\n\t\tif (this.made.size >= (this.options.maxMade ?? 16)) return { refused: 'there are as many games as there may be: join one' }\n\t\tlet name = `${kind}-${this.nextMade++}`\n\t\twhile (this.servers.has(name)) name = `${kind}-${this.nextMade++}`\n\t\tthis.made.set(name, { kind, emptySince: Date.now(), joined: false, matched: !!seat })\n\t\ttry {\n\t\t\tconst room = typeof offered.room === 'function' ? offered.room(settings) : offered.room\n\t\t\tawait this.add(name, { ...room, settings: { ...room.settings, ...settings }, ...(seat && { seat }) })\n\t\t} catch (error) {\n\t\t\tthis.made.delete(name)\n\t\t\tthrow error\n\t\t}\n\t\tthis.logger.info('server', `made the room ${name} (${offered.name})`)\n\t\treturn { room: name }\n\t}\n\n\t/** A room a kind makes, as its listing and matchmaking read it: the one it makes, or one it made once (a kind of a function) */\n\tprivate readonly samples = new Map<string, RoomOptions>()\n\tprivate sampleOf(kind: string): RoomOptions {\n\t\tconst { room } = this.kind(kind)!\n\t\tif (typeof room !== 'function') return room\n\t\tlet sample = this.samples.get(kind)\n\t\tif (!sample) this.samples.set(kind, (sample = room({})))\n\t\treturn sample\n\t}\n\n\t/** How many of a kind's seats are for players: those the computer does not play */\n\tprivate seatsFor(kind: string): number {\n\t\tconst room = this.sampleOf(kind)\n\t\treturn room.mode.seats.filter((seat) => !room.ai?.includes(seat.player)).length\n\t}\n\n\t/** A connection to the queue: it says the kind it waits for, is told how many wait, and its ticket once matched */\n\tprivate waitInQueue(connection: Connection): void {\n\t\tlet waitingFor: string | undefined\n\t\tconst leave = () => {\n\t\t\tif (!waitingFor) return\n\t\t\tconst queue = this.queues.get(waitingFor) ?? []\n\t\t\tconst at = queue.findIndex((each) => each.connection === connection)\n\t\t\tif (at >= 0) queue.splice(at, 1)\n\t\t\tthis.tellQueue(waitingFor)\n\t\t\twaitingFor = undefined\n\t\t}\n\t\t// one that never says what it waits for is let go (RV38)\n\t\tconst within = this.options.joinWithin ?? 10\n\t\tconst deadline = Number.isFinite(within) ? setTimeout(() => !waitingFor && connection.close(), Math.max(0, within) * 1000) : undefined\n\t\tdeadline?.unref?.()\n\t\tconnection.onClose(() => {\n\t\t\tclearTimeout(deadline)\n\t\t\tleave()\n\t\t})\n\t\tconnection.onMessage((text) => {\n\t\t\tlet message: { type?: unknown; kind?: unknown; name?: unknown }\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(text)\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (message.type === 'leave') return leave()\n\t\t\tif (message.type !== 'queue') return\n\t\t\tconst kind = String(message.kind ?? '')\n\t\t\tif (!this.kind(kind)) return connection.send(JSON.stringify({ type: 'refused', reason: `there is no kind of game \"${kind}\"` }))\n\t\t\t// waiting to be matched asks for a game as making one does, from the same few an address has (RV38)\n\t\t\tif (this.askedTooOften(connection.address)) return connection.send(JSON.stringify({ type: 'refused', reason: TOO_OFTEN }))\n\t\t\tclearTimeout(deadline)\n\t\t\tleave()\n\t\t\twaitingFor = kind\n\t\t\tconst queue = this.queues.get(kind) ?? []\n\t\t\tthis.queues.set(kind, queue)\n\t\t\tqueue.push({ connection, ...(typeof message.name === 'string' && { name: message.name.slice(0, 24) }) })\n\t\t\t// whatever goes wrong making the room is told to who waits, and never stops the process\n\t\t\tthis.match(kind).catch((error: unknown) => this.logger.error('server', 'matchmaking failed', { error: String(error) }))\n\t\t})\n\t}\n\n\t/**\n\t * Whether an address has asked for games too often to ask again now: a few a minute each, made (`POST /games`) or\n\t * waited for in the queue, so that nobody fills every place there is by asking over and over. Counted when it has not\n\t */\n\tprivate askedTooOften(address: string): boolean {\n\t\tconst now = Date.now()\n\t\tconst recent = (this.askedBy.get(address) ?? []).filter((at) => now - at < 60_000)\n\t\tif (recent.length >= 3) return true\n\t\trecent.push(now)\n\t\tthis.askedBy.set(address, recent)\n\t\tif (this.askedBy.size > 1024) for (const [each, times] of this.askedBy) if (times.every((at) => now - at >= 60_000)) this.askedBy.delete(each)\n\t\treturn false\n\t}\n\n\t/** Tells everyone waiting for a kind how many wait, of how many it takes */\n\tprivate tellQueue(kind: string): void {\n\t\tconst queue = this.queues.get(kind) ?? []\n\t\tconst needed = this.seatsFor(kind)\n\t\tfor (const each of queue) each.connection.send(JSON.stringify({ type: 'queued', kind, waiting: queue.length, needed }))\n\t}\n\n\t/** As many wait for a kind as its seats: a room of it for them, which lets in only them, and a ticket each */\n\tprivate async match(kind: string): Promise<void> {\n\t\tconst queue = this.queues.get(kind)!\n\t\tconst needed = this.seatsFor(kind)\n\t\tif (queue.length < needed) return this.tellQueue(kind)\n\t\tconst players = queue.splice(0, needed)\n\t\tconst room = this.sampleOf(kind)\n\t\tconst seats = room.mode.seats.filter((seat) => !room.ai?.includes(seat.player))\n\t\t// a ticket each, for the seat it is to: nobody else gets in\n\t\tconst tickets = new Map<string, { player: number; name?: string }>(\n\t\t\tplayers.map((player, i) => [crypto.randomUUID(), { player: seats[i].player, name: player.name }])\n\t\t)\n\t\tconst made = await this.make(kind, {}, (request) => {\n\t\t\tconst ticket = request.ticket ? tickets.get(request.ticket) : undefined\n\t\t\treturn ticket ? { player: ticket.player, name: ticket.name, id: request.ticket } : { refuse: 'this game is for the players matched to it' }\n\t\t}).catch((error: unknown) => ({ refused: error instanceof Error ? error.message : String(error) }))\n\t\tconst given = [...tickets.keys()]\n\t\tplayers.forEach(({ connection }, i) =>\n\t\t\tconnection.send(\n\t\t\t\tJSON.stringify('room' in made ? { type: 'matched', kind, room: made.room, ticket: given[i] } : { type: 'refused', reason: made.refused })\n\t\t\t)\n\t\t)\n\t\tthis.tellQueue(kind)\n\t}\n\n\t/** Closes the rooms players made that have been empty long enough */\n\tprivate async sweep(): Promise<void> {\n\t\tconst now = Date.now()\n\t\tconst emptyFor = (this.options.emptyFor ?? 60) * 1000\n\t\tfor (const [name, made] of [...this.made]) {\n\t\t\tconst server = this.servers.get(name)\n\t\t\tif (!server) continue\n\t\t\tif (server.connections > 0) {\n\t\t\t\tmade.emptySince = now\n\t\t\t\tmade.joined = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// nobody ever came: sooner. A game on: not before those who dropped out may come back to it\n\t\t\tconst wait = !made.joined\n\t\t\t\t? Math.min(emptyFor, 20_000)\n\t\t\t\t: server.phase === 'game'\n\t\t\t\t\t? Math.max(emptyFor, (server.options.abandonAfter ?? 60) * 1000)\n\t\t\t\t\t: emptyFor\n\t\t\tif (now - made.emptySince >= wait) await this.remove(name)\n\t\t}\n\t}\n\n\t/** `POST /games`: a room made of a kind it offers, `{ kind, settings }`, answered with its name. And what a page asks first */\n\tprivate answerMake(request: import('node:http').IncomingMessage, response: import('node:http').ServerResponse): boolean {\n\t\tconst list = this.options.http?.games ?? '/games'\n\t\tconst { pathname } = new URL(request.url ?? '/', 'http://server')\n\t\tif (!list || pathname !== list || (request.method !== 'POST' && request.method !== 'OPTIONS')) return false\n\t\tconst cors = { 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET, POST', 'access-control-allow-headers': 'content-type' }\n\t\tif (request.method === 'OPTIONS') {\n\t\t\tresponse.writeHead(204, cors)\n\t\t\tresponse.end()\n\t\t\treturn true\n\t\t}\n\t\tconst answer = (status: number, body: unknown) => {\n\t\t\tresponse.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store', ...cors })\n\t\t\tresponse.end(JSON.stringify(body))\n\t\t}\n\t\tif (this.askedTooOften(request.socket.remoteAddress ?? '')) {\n\t\t\tanswer(429, { refused: TOO_OFTEN })\n\t\t\treturn true\n\t\t}\n\t\tlet body = ''\n\t\trequest.on('data', (chunk: Buffer) => {\n\t\t\tbody += chunk.toString()\n\t\t\tif (body.length > MAX_BODY) request.destroy()\n\t\t})\n\t\trequest.on('end', () => {\n\t\t\tlet asked: { kind?: unknown; settings?: unknown }\n\t\t\ttry {\n\t\t\t\tasked = JSON.parse(body || '{}')\n\t\t\t} catch {\n\t\t\t\treturn answer(400, { refused: 'what was asked is not JSON' })\n\t\t\t}\n\t\t\tconst settings =\n\t\t\t\tasked.settings && typeof asked.settings === 'object'\n\t\t\t\t\t? Object.fromEntries(Object.entries(asked.settings as Record<string, unknown>).filter(([, value]) => typeof value === 'string'))\n\t\t\t\t\t: {}\n\t\t\tvoid this.make(String(asked.kind ?? ''), settings as Record<string, string>).then(\n\t\t\t\t(made) => answer('room' in made ? 201 : 409, made),\n\t\t\t\t(error: unknown) => answer(500, { refused: error instanceof Error ? error.message : String(error) })\n\t\t\t)\n\t\t})\n\t\treturn true\n\t}\n\n\t/** The port they share */\n\tget port(): number {\n\t\treturn this.socket.port\n\t}\n\n\t/** Whether they take no new games: stopping */\n\tget draining(): boolean {\n\t\treturn [...this.servers.values()].some((room) => room.draining)\n\t}\n\n\t/** The modes the rooms play, each once, in their order: what the ratings are kept by (`/ratings`) */\n\tmodes(): string[] {\n\t\treturn [...new Set([...this.servers.values()].map((room) => room.options.mode.id))]\n\t}\n\n\t/** Every room's game, as a list of games says it (`GET /games`) */\n\tlisting(): GameListing[] {\n\t\treturn [...this.servers].map(([name, room]) => room.listing(name))\n\t}\n\n\t/** The kinds of game players may make a room of (`kinds`), by id, with their names and the settings they offer */\n\tkinds(): GameKindListing[] {\n\t\tconst queue = this.queuePath\n\t\treturn Object.entries(this.options.kinds ?? {}).map(([id, kind]) => ({\n\t\t\tid,\n\t\t\tname: kind.name,\n\t\t\tsettings: { ...this.sampleOf(id).mode.rules.server?.settings },\n\t\t\t...(queue && { queue, waiting: this.queues.get(id)?.length ?? 0, needed: this.seatsFor(id) })\n\t\t}))\n\t}\n\n\t/** The numbers of each room, by its name */\n\tmetrics(): Map<string, ServerMetrics> {\n\t\treturn new Map([...this.servers].map(([name, room]) => [name, room.metrics()]))\n\t}\n\n\t/** Stops every room (see `GameServer.close`: with `drain`, each waits for its game, all at once), then the port */\n\tasync close(options: { drain?: number } = {}): Promise<void> {\n\t\tif (this.sweeper) clearInterval(this.sweeper)\n\t\tfor (const queue of this.queues.values()) for (const { connection } of queue) connection.close()\n\t\tthis.queues.clear()\n\t\tawait Promise.all([...this.servers.values()].map((room) => room.close(options)))\n\t\tawait this.socket.close()\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
//#region src/server/seat.ts
|
|
2
|
+
/** A token to come back to a seat with: what only who has it knows */
|
|
3
|
+
function randomToken() {
|
|
4
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
5
|
+
crypto.getRandomValues(bytes);
|
|
6
|
+
return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A seat of a server's game and who holds it
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
var Seat = class {
|
|
14
|
+
mode;
|
|
15
|
+
computer;
|
|
16
|
+
/** The world's player this seat plays */
|
|
17
|
+
player;
|
|
18
|
+
/** The name shown for the seat: the mode's, or the one its player gave */
|
|
19
|
+
name;
|
|
20
|
+
/** A computer plays it */
|
|
21
|
+
ai;
|
|
22
|
+
/** Someone is in it now */
|
|
23
|
+
connected = false;
|
|
24
|
+
/** Whether the player has said they are ready to start, in the lobby */
|
|
25
|
+
ready = false;
|
|
26
|
+
/** The seat's chosen settings by name, such as its race, team and colour */
|
|
27
|
+
settings;
|
|
28
|
+
/** Who it is rated as (`ratedId`), where the server rates */
|
|
29
|
+
rated;
|
|
30
|
+
/** Its player's rating in the mode (`Rating.rating`), where the server rates */
|
|
31
|
+
rating;
|
|
32
|
+
/** What lets who held it back in: theirs while they hold it, in the lobby and in the game */
|
|
33
|
+
token;
|
|
34
|
+
/** Who holds it, as the `seat` hook said (`SeatAnswer.id`) */
|
|
35
|
+
id;
|
|
36
|
+
/** When what an honest client never sends came from it, in this game: the last `cheating.seconds`' worth, and how many in all */
|
|
37
|
+
cheats = {
|
|
38
|
+
at: [],
|
|
39
|
+
total: 0
|
|
40
|
+
};
|
|
41
|
+
/** The connection of who is in it */
|
|
42
|
+
connection;
|
|
43
|
+
constructor(mode, computer, settings) {
|
|
44
|
+
this.mode = mode;
|
|
45
|
+
this.computer = computer;
|
|
46
|
+
this.player = mode.player;
|
|
47
|
+
this.name = mode.name;
|
|
48
|
+
this.ai = computer;
|
|
49
|
+
this.settings = settings;
|
|
50
|
+
}
|
|
51
|
+
/** Nobody in it, nor holding it in a game to come back to, nor a computer */
|
|
52
|
+
get isFree() {
|
|
53
|
+
return !this.ai && !this.connected && this.token === void 0;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Taken by a connection: whoever was in it before is closed out (a player back from another page), and its token is
|
|
57
|
+
* kept, or made. A name it is given is taken. Gives the token
|
|
58
|
+
*/
|
|
59
|
+
take(connection, name) {
|
|
60
|
+
if (this.connection && this.connection !== connection) this.connection.close();
|
|
61
|
+
this.connected = true;
|
|
62
|
+
this.connection = connection;
|
|
63
|
+
this.token ??= randomToken();
|
|
64
|
+
if (name) this.name = name.slice(0, 24);
|
|
65
|
+
return this.token;
|
|
66
|
+
}
|
|
67
|
+
/** Whoever holds another seat moves here, in the lobby: their connection, token, who they are and their name; not ready */
|
|
68
|
+
takeFrom(other) {
|
|
69
|
+
this.connected = true;
|
|
70
|
+
this.connection = other.connection;
|
|
71
|
+
this.token = other.token;
|
|
72
|
+
this.id = other.id;
|
|
73
|
+
this.rated = other.rated;
|
|
74
|
+
this.name = other.name;
|
|
75
|
+
this.ready = false;
|
|
76
|
+
other.free();
|
|
77
|
+
}
|
|
78
|
+
/** Its connection gone, held for who had it to come back to (a game on): their token, who they are and their name stay */
|
|
79
|
+
drop() {
|
|
80
|
+
this.connected = false;
|
|
81
|
+
this.connection = void 0;
|
|
82
|
+
}
|
|
83
|
+
/** Given up: free for anyone, with the mode's name, nobody's token, not ready */
|
|
84
|
+
free() {
|
|
85
|
+
this.drop();
|
|
86
|
+
this.token = void 0;
|
|
87
|
+
this.id = void 0;
|
|
88
|
+
this.rated = void 0;
|
|
89
|
+
this.ready = false;
|
|
90
|
+
this.name = this.mode.name;
|
|
91
|
+
}
|
|
92
|
+
/** Held no more by who it was: their token and who they are no good (a kick). Their connection is closed by the caller */
|
|
93
|
+
revoke() {
|
|
94
|
+
this.token = void 0;
|
|
95
|
+
this.id = void 0;
|
|
96
|
+
this.rated = void 0;
|
|
97
|
+
}
|
|
98
|
+
/** What a new game starts it with: nobody's cheats, not ready, the computer's where the server says */
|
|
99
|
+
newGame() {
|
|
100
|
+
this.cheats = {
|
|
101
|
+
at: [],
|
|
102
|
+
total: 0
|
|
103
|
+
};
|
|
104
|
+
this.ready = false;
|
|
105
|
+
this.ai = this.computer;
|
|
106
|
+
}
|
|
107
|
+
/** New, for a new game with nobody: free, and as a new game starts it */
|
|
108
|
+
reset() {
|
|
109
|
+
this.free();
|
|
110
|
+
this.newGame();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
//#endregion
|
|
114
|
+
export { Seat, randomToken };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=seat.js.map
|