@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
|
@@ -0,0 +1,1394 @@
|
|
|
1
|
+
import { footprintOf } from "../defs.js";
|
|
2
|
+
import { worldFailed } from "../engineEvents.js";
|
|
3
|
+
import { toMessage } from "../messages.js";
|
|
4
|
+
import { hasGroundSpeeds, navigationSpeedOn } from "../groundSpeed.js";
|
|
5
|
+
import { groundKnownTo } from "../cellLayers.js";
|
|
6
|
+
import { formationPreview } from "../formation.js";
|
|
7
|
+
import { BuildGrid } from "../grid.js";
|
|
8
|
+
import { placementCells, siteOf } from "../orders/build.js";
|
|
9
|
+
import { errorFields } from "../log.js";
|
|
10
|
+
import { mergeValues, redefineIn, redefinitions, restoreRedefinitions } from "../gameData.js";
|
|
11
|
+
import { loadTerrain, saveTerrain } from "../terrainSave.js";
|
|
12
|
+
import { MissingGround, applyGroundChanges, applyTerrainPatch, groundOf, isGroundChanges, savedGround, terrainPatchProblem } from "../ground.js";
|
|
13
|
+
import { RecastNavigation } from "../navigation/recast.js";
|
|
14
|
+
import { AirNavigation } from "../navigation/air.js";
|
|
15
|
+
import { createRegistry } from "../game.js";
|
|
16
|
+
import { isServerMessage, readHostMessage } from "./wire.js";
|
|
17
|
+
import { isFiniteNumber, protocolMismatch, readMessage } from "./protocol.js";
|
|
18
|
+
import { apply, bytesByKey, diff, quantize } from "./delta.js";
|
|
19
|
+
import { LocalDebug, RemoteDebug, askDebug } from "./debug.js";
|
|
20
|
+
import { LocalSession, endSharedCaptures, shareCaptures } from "./local.js";
|
|
21
|
+
//#region src/session/remote.ts
|
|
22
|
+
/**
|
|
23
|
+
* Two ends of a connection in the page, `[client, host]`: everything sent is text, delivered after the latency the
|
|
24
|
+
* options give. The whole networked way, in one page and in tests
|
|
25
|
+
*
|
|
26
|
+
* @beta
|
|
27
|
+
*/
|
|
28
|
+
function loopback(options = {}) {
|
|
29
|
+
const { latency = 0, jitter = 0, loss = 0, random = Math.random } = options;
|
|
30
|
+
/** One end: what it is handed to deliver, and the transport its side holds */
|
|
31
|
+
class End {
|
|
32
|
+
lossy;
|
|
33
|
+
peer;
|
|
34
|
+
bytesSent = 0;
|
|
35
|
+
messagesSent = 0;
|
|
36
|
+
handler = () => {};
|
|
37
|
+
closed = () => {};
|
|
38
|
+
open = true;
|
|
39
|
+
/** When the last message sent is due: none overtakes the one before */
|
|
40
|
+
last = 0;
|
|
41
|
+
constructor(lossy) {
|
|
42
|
+
this.lossy = lossy;
|
|
43
|
+
}
|
|
44
|
+
send(message) {
|
|
45
|
+
if (!this.open) return;
|
|
46
|
+
this.bytesSent += message.length;
|
|
47
|
+
this.messagesSent++;
|
|
48
|
+
if (this.lossy && loss > 0 && message.startsWith("{\"type\":\"delta\"") && random() < loss) return;
|
|
49
|
+
const due = Math.max(performance.now() + latency + random() * jitter, this.last);
|
|
50
|
+
this.last = due;
|
|
51
|
+
const { peer } = this;
|
|
52
|
+
setTimeout(() => peer.deliver(message), Math.max(due - performance.now(), 0));
|
|
53
|
+
}
|
|
54
|
+
deliver(message) {
|
|
55
|
+
if (this.open) this.handler(message);
|
|
56
|
+
}
|
|
57
|
+
onMessage(handler) {
|
|
58
|
+
this.handler = handler;
|
|
59
|
+
}
|
|
60
|
+
onClose(handler) {
|
|
61
|
+
this.closed = handler;
|
|
62
|
+
}
|
|
63
|
+
close() {
|
|
64
|
+
this.shut();
|
|
65
|
+
const { peer } = this;
|
|
66
|
+
setTimeout(() => peer.shut(), Math.max(this.last - performance.now(), latency, 0));
|
|
67
|
+
}
|
|
68
|
+
shut() {
|
|
69
|
+
if (!this.open) return;
|
|
70
|
+
this.open = false;
|
|
71
|
+
this.closed();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const client = new End(false);
|
|
75
|
+
const host = new End(true);
|
|
76
|
+
client.peer = host;
|
|
77
|
+
host.peer = client;
|
|
78
|
+
return [client, host];
|
|
79
|
+
}
|
|
80
|
+
/** Whether what a player sent as a command is one to look at: an object with a type. Its fields are its definition's to check */
|
|
81
|
+
var isCommand = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
|
|
82
|
+
/**
|
|
83
|
+
* Runs of equal values: how many 0s, then how many 1s, and so on
|
|
84
|
+
*
|
|
85
|
+
* @internal
|
|
86
|
+
*/
|
|
87
|
+
function toRuns(cells) {
|
|
88
|
+
const runs = [];
|
|
89
|
+
let value = 0;
|
|
90
|
+
let count = 0;
|
|
91
|
+
for (let i = 0; i < cells.length; i++) {
|
|
92
|
+
const cell = cells[i] ? 1 : 0;
|
|
93
|
+
if (cell === value) count++;
|
|
94
|
+
else {
|
|
95
|
+
runs.push(count);
|
|
96
|
+
value = cell;
|
|
97
|
+
count = 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
runs.push(count);
|
|
101
|
+
return runs;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Cells back from `toRuns`, into `out`
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
function fromRuns(runs, out) {
|
|
109
|
+
let at = 0;
|
|
110
|
+
let value = 0;
|
|
111
|
+
for (const run of runs) {
|
|
112
|
+
out.fill(value, at, Math.min(at + run, out.length));
|
|
113
|
+
at += run;
|
|
114
|
+
value ^= 1;
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/** The fog for the wire: nothing of its cells when the whole map is lit, which they say nothing about then */
|
|
119
|
+
var fogMessage = (fog) => fog.revealed ? {
|
|
120
|
+
revealed: true,
|
|
121
|
+
visible: [],
|
|
122
|
+
explored: []
|
|
123
|
+
} : {
|
|
124
|
+
revealed: false,
|
|
125
|
+
visible: toRuns(fog.visible),
|
|
126
|
+
explored: toRuns(fog.explored)
|
|
127
|
+
};
|
|
128
|
+
/** The runs of a team's fog, by its cells, as they were at a version of the fog: the seats of a team share them */
|
|
129
|
+
var fogRuns = /* @__PURE__ */ new WeakMap();
|
|
130
|
+
var revealedFog = {
|
|
131
|
+
revealed: true,
|
|
132
|
+
visible: [],
|
|
133
|
+
explored: []
|
|
134
|
+
};
|
|
135
|
+
/** The fog for the wire, its runs made once for every seat of a team while it stays as it is */
|
|
136
|
+
function fogMessageOf(fog) {
|
|
137
|
+
if (fog.revealed) return revealedFog;
|
|
138
|
+
const known = fogRuns.get(fog.visible);
|
|
139
|
+
if (known && known.version === fog.version) return known.message;
|
|
140
|
+
const message = fogMessage(fog);
|
|
141
|
+
fogRuns.set(fog.visible, {
|
|
142
|
+
version: fog.version,
|
|
143
|
+
message
|
|
144
|
+
});
|
|
145
|
+
return message;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* What each state told was rounded to (`quantize`), by the very object: a state that did not change is the object it
|
|
149
|
+
* was (`LocalSession` hands back what it told while it still says the truth), so it is rounded once and stays the same
|
|
150
|
+
* object round after round, which the diff then passes over without looking inside (`diff`)
|
|
151
|
+
*/
|
|
152
|
+
var quantizedStates = /* @__PURE__ */ new WeakMap();
|
|
153
|
+
function quantizedOf(state) {
|
|
154
|
+
let made = quantizedStates.get(state);
|
|
155
|
+
if (!made) quantizedStates.set(state, made = quantize(state));
|
|
156
|
+
return made;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The entities of a frame as the wire has them (`quantize`), one list between the seats of a round told the very same
|
|
160
|
+
* (the seats of a side, the observers: what they are told of an entity is one object between them, `LocalSession`), so
|
|
161
|
+
* that it is rounded once and a diff of the same two lists is worked out once (`diff`). `made` is this round's so far
|
|
162
|
+
*/
|
|
163
|
+
function quantizedList(entities, made) {
|
|
164
|
+
for (const { told, sent } of made) {
|
|
165
|
+
if (told.length !== entities.length) continue;
|
|
166
|
+
let alike = true;
|
|
167
|
+
for (let i = 0; i < told.length; i++) if (told[i] !== entities[i]) {
|
|
168
|
+
alike = false;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (alike) return sent;
|
|
172
|
+
}
|
|
173
|
+
const sent = entities.map(quantizedOf);
|
|
174
|
+
made.push({
|
|
175
|
+
told: entities,
|
|
176
|
+
sent
|
|
177
|
+
});
|
|
178
|
+
return sent;
|
|
179
|
+
}
|
|
180
|
+
/** Milliseconds between two pings, to measure the time to the host and back */
|
|
181
|
+
var PING_EVERY = 2e3;
|
|
182
|
+
/** Seconds of jitter the interpolation buffer makes room for, at most */
|
|
183
|
+
var MAX_JITTER = .2;
|
|
184
|
+
/**
|
|
185
|
+
* Seconds with no snapshot after which the host is taken to have gone quiet, at the least (three of its usual gaps
|
|
186
|
+
* between two where they are longer), as it does while its world stands still (a replay held, a game paused): the
|
|
187
|
+
* screen then comes to the newest snapshot it has, there being nothing to make room for
|
|
188
|
+
*/
|
|
189
|
+
var QUIET = .5;
|
|
190
|
+
var emptyTiming = (since, byKey = false) => ({
|
|
191
|
+
since,
|
|
192
|
+
ticks: 0,
|
|
193
|
+
tickTime: 0,
|
|
194
|
+
tickMax: 0,
|
|
195
|
+
sends: 0,
|
|
196
|
+
sendTime: 0,
|
|
197
|
+
sendMax: 0,
|
|
198
|
+
parts: {
|
|
199
|
+
capture: 0,
|
|
200
|
+
quantize: 0,
|
|
201
|
+
fog: 0,
|
|
202
|
+
diff: 0,
|
|
203
|
+
stringify: 0
|
|
204
|
+
},
|
|
205
|
+
bytes: 0,
|
|
206
|
+
...byKey && { keys: {} }
|
|
207
|
+
});
|
|
208
|
+
/** Frames kept on either side, at most: more than a second and a half of ticks a player has not said it got */
|
|
209
|
+
var KEPT_FRAMES = 64;
|
|
210
|
+
/**
|
|
211
|
+
* Bytes of the frames kept for one player, at most (`SessionHostOptions.keptBytes`): 8 MB, some thirty frames of a
|
|
212
|
+
* thousand units (a frame of them is about 250 KB). A player who has said nothing for longer is sent everything again
|
|
213
|
+
*/
|
|
214
|
+
var KEPT_BYTES = 8388608;
|
|
215
|
+
/** What each list of entities a frame keeps takes as the wire writes it: one list between the seats of a side (`quantizedList`) */
|
|
216
|
+
var listBytes = /* @__PURE__ */ new WeakMap();
|
|
217
|
+
/** Entities of a list written out to tell what the list takes: the rest are taken to be as big */
|
|
218
|
+
var SAMPLED = 8;
|
|
219
|
+
/**
|
|
220
|
+
* What a frame kept for a player takes, about: its entities as the wire writes them (some of them written out, the rest
|
|
221
|
+
* taken to be as big: writing out all of a thousand units' at every send costs more than the send itself), its fog,
|
|
222
|
+
* its players
|
|
223
|
+
*/
|
|
224
|
+
function frameBytes(frame) {
|
|
225
|
+
const { entities, players } = frame.snapshot;
|
|
226
|
+
let bytes = listBytes.get(entities);
|
|
227
|
+
if (bytes === void 0) {
|
|
228
|
+
const sampled = Math.min(entities.length, SAMPLED);
|
|
229
|
+
let written = 0;
|
|
230
|
+
for (let i = 0; i < sampled; i++) written += JSON.stringify(entities[Math.floor(i * entities.length / sampled)]).length;
|
|
231
|
+
listBytes.set(entities, bytes = sampled ? Math.round(written / sampled * entities.length) : 0);
|
|
232
|
+
}
|
|
233
|
+
let fog = listBytes.get(frame.fog);
|
|
234
|
+
if (fog === void 0) listBytes.set(frame.fog, fog = JSON.stringify(frame.fog).length);
|
|
235
|
+
let told = listBytes.get(players);
|
|
236
|
+
if (told === void 0) listBytes.set(players, told = JSON.stringify(players).length);
|
|
237
|
+
return bytes + fog + told;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Snapshots waiting for the screen, at most (`RemoteSession.update`): three seconds of them at 20 a second. A page
|
|
241
|
+
* whose screen stops asking (a tab in the background) folds the oldest into the next, their events kept, rather than
|
|
242
|
+
* keeping every one for as long as it is away
|
|
243
|
+
*/
|
|
244
|
+
var KEPT_SNAPSHOTS = 64;
|
|
245
|
+
/** Events the folded snapshots carry together, at most: the newest kept. What is the screen's only (the state is in the snapshot) */
|
|
246
|
+
var KEPT_SCREEN_EVENTS = 2e3;
|
|
247
|
+
/**
|
|
248
|
+
* Runs a world for players connected to it over transports: the server's job, done anywhere. It steps the world by
|
|
249
|
+
* the clock (`start`) or by hand (`update`), and at most `sendRate` times a second sends every player what changed in
|
|
250
|
+
* what they may know (`Delta`), as their `LocalSession` seat sees it: against the last tick they said they got, with
|
|
251
|
+
* the events they have not. Their commands come in as they arrive, and go in at the next tick
|
|
252
|
+
*
|
|
253
|
+
* @beta
|
|
254
|
+
*/
|
|
255
|
+
var SessionHost = class {
|
|
256
|
+
world;
|
|
257
|
+
seats = /* @__PURE__ */ new Set();
|
|
258
|
+
accumulator = 0;
|
|
259
|
+
sinceSent = Infinity;
|
|
260
|
+
timer;
|
|
261
|
+
last = 0;
|
|
262
|
+
sendEvery;
|
|
263
|
+
/** Simulation speed multiplier, 0 pauses */
|
|
264
|
+
speed = 1;
|
|
265
|
+
constructor(world, options = {}) {
|
|
266
|
+
this.world = world;
|
|
267
|
+
this.sendEvery = 1 / (options.sendRate ?? 20);
|
|
268
|
+
this.maxTicks = options.maxTicksPerUpdate ?? 8;
|
|
269
|
+
this.onSlowTick = options.onSlowTick;
|
|
270
|
+
this.ownsWorld = options.ownsWorld ?? false;
|
|
271
|
+
this.dropSilentAfter = options.dropSilentAfter ?? 15;
|
|
272
|
+
this.keptEvents = options.keptEvents ?? 600;
|
|
273
|
+
this.keptFrames = Math.max(2, options.keptFrames ?? KEPT_FRAMES);
|
|
274
|
+
this.keptBytes = Math.max(1, options.keptBytes ?? KEPT_BYTES);
|
|
275
|
+
this.groundPart = Math.max(1, options.groundPart ?? GROUND_PART);
|
|
276
|
+
this.byKey = options.bytesByKey ?? false;
|
|
277
|
+
this.timing = emptyTiming(performance.now(), this.byKey);
|
|
278
|
+
this.onError = options.onError;
|
|
279
|
+
this.stopFailed = world.events.on(worldFailed, (failure) => {
|
|
280
|
+
for (const seat of this.seats) this.post(seat, {
|
|
281
|
+
type: "failed",
|
|
282
|
+
message: failure.message
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
onError;
|
|
287
|
+
stopFailed;
|
|
288
|
+
dropSilentAfter;
|
|
289
|
+
keptFrames;
|
|
290
|
+
keptBytes;
|
|
291
|
+
groundPart;
|
|
292
|
+
keptEvents;
|
|
293
|
+
/** Whether the characters sent are counted by what they carry (`bytesByKey`) */
|
|
294
|
+
byKey;
|
|
295
|
+
ownsWorld;
|
|
296
|
+
maxTicks;
|
|
297
|
+
onSlowTick;
|
|
298
|
+
/**
|
|
299
|
+
* Seats a player at the world, on the host's end of a transport. The welcome goes out at once: everything the player
|
|
300
|
+
* may know, also to one who comes back. `readOnly` for who only watches: their commands go nowhere but their lines
|
|
301
|
+
* of chat and their pings, which go to who watches alone (`observers`, said by `name`); and with `observer` they see
|
|
302
|
+
* everything, from `player`'s side. `allow` decides on each command before it goes in: a limit of how many, a game
|
|
303
|
+
* paused. One it lets go is answered, refused (the `command` event of its `seq`): with what it gives for why, or
|
|
304
|
+
* `cannotDoThat` for `false`. `inOrder` for a transport that loses nothing and keeps the order (a worker's messages):
|
|
305
|
+
* each frame is the base of the next once it went, with no answer waited for, so the player always gets what changed
|
|
306
|
+
* since the frame it has, and takes it in without looking over the rest
|
|
307
|
+
*/
|
|
308
|
+
connect(player, transport, options = {}) {
|
|
309
|
+
const session = new LocalSession(this.world, player, { observer: options.observer });
|
|
310
|
+
const seat = {
|
|
311
|
+
transport,
|
|
312
|
+
session,
|
|
313
|
+
frames: /* @__PURE__ */ new Map(),
|
|
314
|
+
frameSizes: /* @__PURE__ */ new Map(),
|
|
315
|
+
keptBytes: 0,
|
|
316
|
+
acked: -1,
|
|
317
|
+
heardUpTo: -1,
|
|
318
|
+
events: [],
|
|
319
|
+
eventsMade: 0,
|
|
320
|
+
sentAt: /* @__PURE__ */ new Map(),
|
|
321
|
+
roundTrip: 100,
|
|
322
|
+
heard: this.world.tick,
|
|
323
|
+
answers: [],
|
|
324
|
+
groundSent: /* @__PURE__ */ new Set(),
|
|
325
|
+
inOrder: options.inOrder ?? false
|
|
326
|
+
};
|
|
327
|
+
this.seats.add(seat);
|
|
328
|
+
transport.onMessage((text) => {
|
|
329
|
+
const message = readMessage(text);
|
|
330
|
+
if (!message) return;
|
|
331
|
+
if (message.type === "ack") {
|
|
332
|
+
if (isFiniteNumber(message.tick)) this.acknowledge(seat, message.tick, isFiniteNumber(message.events) ? message.events : void 0);
|
|
333
|
+
} else if (message.type === "ping") {
|
|
334
|
+
if (isFiniteNumber(message.at)) this.post(seat, {
|
|
335
|
+
type: "pong",
|
|
336
|
+
at: message.at
|
|
337
|
+
});
|
|
338
|
+
} else if (message.type === "ground") {
|
|
339
|
+
const parts = this.groundParts();
|
|
340
|
+
const { part } = message;
|
|
341
|
+
if (parts && Number.isInteger(part) && part >= 0 && part < parts.length && !seat.groundSent.has(part)) {
|
|
342
|
+
seat.groundSent.add(part);
|
|
343
|
+
this.post(seat, {
|
|
344
|
+
type: "ground",
|
|
345
|
+
part,
|
|
346
|
+
parts: parts.length,
|
|
347
|
+
text: parts[part]
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
} else if (message.type === "debug") {
|
|
351
|
+
if (!this.world.debugPlayers.has(player)) return;
|
|
352
|
+
seat.debug ??= {
|
|
353
|
+
view: new LocalDebug(this.world),
|
|
354
|
+
sentAt: -Infinity
|
|
355
|
+
};
|
|
356
|
+
askDebug(seat.debug.view, message);
|
|
357
|
+
seat.debug.sentAt = -Infinity;
|
|
358
|
+
} else if (message.type === "command" && options.readOnly) {
|
|
359
|
+
const { command } = message;
|
|
360
|
+
if (!isCommand(command) || command.type !== "chat" && command.type !== "ping") return;
|
|
361
|
+
session.send({
|
|
362
|
+
...command,
|
|
363
|
+
to: "observers",
|
|
364
|
+
by: options.name ?? "A watcher"
|
|
365
|
+
});
|
|
366
|
+
this.heardOutside = true;
|
|
367
|
+
} else if (message.type === "command") {
|
|
368
|
+
const allowed = options.allow?.() ?? true;
|
|
369
|
+
const seq = isFiniteNumber(message.seq) ? message.seq : void 0;
|
|
370
|
+
if (!isCommand(message.command)) return;
|
|
371
|
+
if ((message.command.type === "chat" || message.command.type === "ping") && message.command.to === "observers") return;
|
|
372
|
+
if (allowed === true) {
|
|
373
|
+
session.send(message.command, seq);
|
|
374
|
+
if (this.world.registry.command(message.command.type)?.outsideTick) this.heardOutside = true;
|
|
375
|
+
} else if (seq !== void 0) seat.answers.push({
|
|
376
|
+
type: "command",
|
|
377
|
+
seq,
|
|
378
|
+
refusal: allowed ? toMessage(allowed) : { key: "cannotDoThat" }
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
transport.onClose(() => this.leave(seat));
|
|
383
|
+
const { registry } = this.world;
|
|
384
|
+
this.post(seat, {
|
|
385
|
+
type: "welcome",
|
|
386
|
+
protocol: 13,
|
|
387
|
+
player,
|
|
388
|
+
tickRate: this.world.tickRate,
|
|
389
|
+
game: registry.game,
|
|
390
|
+
hash: registry.dataHash,
|
|
391
|
+
terrain: savedGround(groundKnownTo(this.world, options.observer ? void 0 : this.world.player(player).team), this.world.ground, this.world.registry.module?.maps, this.world.registry.module ?? {}),
|
|
392
|
+
...this.world.map.environment && { environment: this.world.map.environment },
|
|
393
|
+
...this.world.map.doodads.length > 0 && { doodads: [...this.world.map.doodads] },
|
|
394
|
+
...this.world.map.regions.length > 0 && { regions: [...this.world.map.regions] },
|
|
395
|
+
...this.world.map.cameras.length > 0 && { cameras: [...this.world.map.cameras] },
|
|
396
|
+
...this.world.map.paths.length > 0 && { paths: [...this.world.map.paths] },
|
|
397
|
+
...this.world.states.has(redefinitions) && { redefinitions: structuredClone(this.world.state(redefinitions)) },
|
|
398
|
+
first: this.deltaFor(seat),
|
|
399
|
+
...options.observer ? { observer: true } : {},
|
|
400
|
+
...this.world.debugPlayers.has(player) && { debug: true }
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/** The ground the world started as, whole, in parts small enough to go one at a time: made the first time one is asked for */
|
|
404
|
+
groundParts() {
|
|
405
|
+
const base = this.world.ground;
|
|
406
|
+
if (!base) return void 0;
|
|
407
|
+
if (!this.ground) {
|
|
408
|
+
const text = JSON.stringify(saveTerrain(base.terrain));
|
|
409
|
+
this.ground = [];
|
|
410
|
+
for (let i = 0; i < text.length; i += this.groundPart) this.ground.push(text.slice(i, i + this.groundPart));
|
|
411
|
+
}
|
|
412
|
+
return this.ground;
|
|
413
|
+
}
|
|
414
|
+
ground;
|
|
415
|
+
/** The player has the frame of `tick`: what comes next is made against it, and what it has is not sent again */
|
|
416
|
+
acknowledge(seat, tick, events) {
|
|
417
|
+
if (events !== void 0) seat.events = seat.events.filter((sent) => sent.n > events);
|
|
418
|
+
if (tick <= seat.heardUpTo || tick > this.world.tick) return;
|
|
419
|
+
seat.heardUpTo = tick;
|
|
420
|
+
seat.heard = this.world.tick;
|
|
421
|
+
const sent = seat.sentAt.get(tick) ?? (seat.whole?.tick === tick ? seat.whole.at : void 0);
|
|
422
|
+
if (sent !== void 0) seat.roundTrip += (performance.now() - sent - seat.roundTrip) * .2;
|
|
423
|
+
if (seat.whole && tick >= seat.whole.tick) seat.whole = void 0;
|
|
424
|
+
for (const kept of seat.sentAt.keys()) if (kept <= tick) seat.sentAt.delete(kept);
|
|
425
|
+
seat.events = seat.events.filter((sent) => sent.tick > tick);
|
|
426
|
+
if (!seat.frames.has(tick)) return;
|
|
427
|
+
seat.acked = tick;
|
|
428
|
+
for (const kept of [...seat.frames.keys()]) if (kept < tick) this.forget(seat, kept);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Whether a player is sent nothing this round: it has no frame kept to make a delta against, and the whole one it
|
|
432
|
+
* was sent last is on its way still. Another would be as big, and come no sooner than the answer to the first
|
|
433
|
+
*/
|
|
434
|
+
holding(seat, now) {
|
|
435
|
+
return !!seat.whole && !seat.frames.has(seat.acked) && now - seat.whole.at <= seat.roundTrip * 1.5 + this.sendEvery * 1e3;
|
|
436
|
+
}
|
|
437
|
+
leave(seat) {
|
|
438
|
+
if (!this.seats.delete(seat)) return;
|
|
439
|
+
seat.session.dispose();
|
|
440
|
+
seat.debug?.view.dispose();
|
|
441
|
+
if (this.seats.size === 0) this.stop();
|
|
442
|
+
}
|
|
443
|
+
/** How many players are connected */
|
|
444
|
+
get players() {
|
|
445
|
+
return this.seats.size;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* How much the host keeps for each connected player, for a test of its limits (`keptFrames`, `keptEvents`): the
|
|
449
|
+
* frames it may make a delta against, when each was sent, and the events not yet said to be had
|
|
450
|
+
*
|
|
451
|
+
* @internal
|
|
452
|
+
*/
|
|
453
|
+
kept() {
|
|
454
|
+
return [...this.seats].map((seat) => ({
|
|
455
|
+
frames: seat.frames.size,
|
|
456
|
+
bytes: seat.keptBytes,
|
|
457
|
+
sentAt: seat.sentAt.size,
|
|
458
|
+
events: seat.events.length
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
post(seat, message) {
|
|
462
|
+
seat.transport.send(JSON.stringify(message));
|
|
463
|
+
}
|
|
464
|
+
/** Lets `dt` seconds pass: the ticks that fit into it, then what changed to everyone, when it is time to send */
|
|
465
|
+
update(dt) {
|
|
466
|
+
const { world } = this;
|
|
467
|
+
let stepped = false;
|
|
468
|
+
this.accumulator += dt * this.speed * world.cheats.speed;
|
|
469
|
+
if (world.cheats.speed === 0 && this.speed > 0 && world.debugWaiting) this.accumulator += world.dt;
|
|
470
|
+
this.sinceSent += dt;
|
|
471
|
+
let ticks = 0;
|
|
472
|
+
while (this.accumulator >= world.dt) {
|
|
473
|
+
if (ticks++ >= this.maxTicks) {
|
|
474
|
+
this.accumulator = 0;
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
const began = performance.now();
|
|
478
|
+
world.step();
|
|
479
|
+
const took = performance.now() - began;
|
|
480
|
+
const { timing, totals } = this;
|
|
481
|
+
timing.ticks++;
|
|
482
|
+
timing.tickTime += took;
|
|
483
|
+
timing.tickMax = Math.max(timing.tickMax, took);
|
|
484
|
+
totals.ticks++;
|
|
485
|
+
totals.tickTime += took;
|
|
486
|
+
totals.lastTick = took;
|
|
487
|
+
if (took > world.dt * 1e3) this.onSlowTick?.(took, world.tick);
|
|
488
|
+
this.accumulator -= world.dt;
|
|
489
|
+
stepped = true;
|
|
490
|
+
}
|
|
491
|
+
if (stepped || this.heardOutside) this.sendDue();
|
|
492
|
+
}
|
|
493
|
+
/** What the debug tools of a seat follow, as the world stands: when they follow, list or log anything */
|
|
494
|
+
sendDebug(seat, debug, now) {
|
|
495
|
+
debug.sentAt = now;
|
|
496
|
+
const { view } = debug;
|
|
497
|
+
view.refresh();
|
|
498
|
+
if (!view.inspection && !view.world && !view.logging) return;
|
|
499
|
+
this.post(seat, {
|
|
500
|
+
type: "debug",
|
|
501
|
+
inspection: view.inspection,
|
|
502
|
+
world: view.world,
|
|
503
|
+
log: view.logging ? [...view.log()] : []
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
/** Moves the game on by `ticks` at the next update, however fast it goes: a step of a held game (`speed` 0) */
|
|
507
|
+
advance(ticks = 1) {
|
|
508
|
+
this.accumulator += this.world.dt * Math.max(0, Math.floor(ticks));
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Sends everyone what changed, when it is time to, for a world something else steps: the page's own game, shared with
|
|
512
|
+
* those who watch it (`shareGame`). `dt` is the seconds since the last call. Nothing goes while the world stands still
|
|
513
|
+
*/
|
|
514
|
+
publish(dt) {
|
|
515
|
+
this.sinceSent += dt;
|
|
516
|
+
if (this.world.tick !== this.sentTick || this.heardOutside) this.sendDue();
|
|
517
|
+
}
|
|
518
|
+
/** The tick what changed went out at last */
|
|
519
|
+
sentTick = -1;
|
|
520
|
+
/** Talk was carried out since what changed went out last: it goes out with no tick to wait for (RV14) */
|
|
521
|
+
heardOutside = false;
|
|
522
|
+
/** What changed to everyone, when the rate allows */
|
|
523
|
+
sendDue() {
|
|
524
|
+
if (this.sinceSent < this.sendEvery) return;
|
|
525
|
+
this.sentTick = this.world.tick;
|
|
526
|
+
this.heardOutside = false;
|
|
527
|
+
this.sinceSent = Math.min(this.sinceSent - this.sendEvery, this.sendEvery);
|
|
528
|
+
const began = performance.now();
|
|
529
|
+
const silent = this.dropSilentAfter * this.world.tickRate;
|
|
530
|
+
shareCaptures(this.world);
|
|
531
|
+
this.roundEntities = [];
|
|
532
|
+
try {
|
|
533
|
+
for (const seat of [...this.seats]) {
|
|
534
|
+
if (this.world.tick - seat.heard > silent) {
|
|
535
|
+
seat.transport.close();
|
|
536
|
+
this.leave(seat);
|
|
537
|
+
} else if (this.holding(seat, began)) this.sentTick = -1;
|
|
538
|
+
else {
|
|
539
|
+
const delta = this.deltaFor(seat);
|
|
540
|
+
const made = performance.now();
|
|
541
|
+
const text = JSON.stringify({
|
|
542
|
+
type: "delta",
|
|
543
|
+
...delta
|
|
544
|
+
});
|
|
545
|
+
this.timing.parts.stringify += performance.now() - made;
|
|
546
|
+
this.timing.bytes += text.length;
|
|
547
|
+
if (this.timing.keys) bytesByKey(delta, text, this.timing.keys);
|
|
548
|
+
seat.transport.send(text);
|
|
549
|
+
}
|
|
550
|
+
if (seat.debug && began - seat.debug.sentAt >= 100) this.sendDebug(seat, seat.debug, began);
|
|
551
|
+
}
|
|
552
|
+
} finally {
|
|
553
|
+
endSharedCaptures(this.world);
|
|
554
|
+
this.roundEntities = [];
|
|
555
|
+
}
|
|
556
|
+
const took = performance.now() - began;
|
|
557
|
+
const { timing, totals } = this;
|
|
558
|
+
timing.sends++;
|
|
559
|
+
timing.sendTime += took;
|
|
560
|
+
timing.sendMax = Math.max(timing.sendMax, took);
|
|
561
|
+
totals.sends++;
|
|
562
|
+
totals.sendTime += took;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Where the host's time went since it was last asked (`takeTiming`): ticks of the simulation, and rounds of sending
|
|
566
|
+
* everyone what changed (the snapshots, the diffs, the JSON), each in milliseconds, added up and at most
|
|
567
|
+
*/
|
|
568
|
+
timing;
|
|
569
|
+
/**
|
|
570
|
+
* Where the host's time went since it started, never counted anew: ticks and rounds of sending, with the
|
|
571
|
+
* milliseconds they took, and how long the last tick took. What a server's numbers are made of (`/metrics`)
|
|
572
|
+
*/
|
|
573
|
+
totals = {
|
|
574
|
+
ticks: 0,
|
|
575
|
+
tickTime: 0,
|
|
576
|
+
lastTick: 0,
|
|
577
|
+
sends: 0,
|
|
578
|
+
sendTime: 0
|
|
579
|
+
};
|
|
580
|
+
/** Where the time went since the last call, and starts counting anew: for a server to print */
|
|
581
|
+
takeTiming() {
|
|
582
|
+
const now = performance.now();
|
|
583
|
+
const timing = {
|
|
584
|
+
...this.timing,
|
|
585
|
+
seconds: (now - this.timing.since) / 1e3
|
|
586
|
+
};
|
|
587
|
+
this.timing = emptyTiming(now, this.byKey);
|
|
588
|
+
return timing;
|
|
589
|
+
}
|
|
590
|
+
/** The lists of entities told this round so far, and what each was sent as, for the seats told the same to share one (`quantizedList`) */
|
|
591
|
+
roundEntities = [];
|
|
592
|
+
/** What the player knows now, as what changed since the tick they last said they got */
|
|
593
|
+
deltaFor(seat) {
|
|
594
|
+
const { session } = seat;
|
|
595
|
+
const { parts } = this.timing;
|
|
596
|
+
const began = performance.now();
|
|
597
|
+
const snapshot = session.snapshot();
|
|
598
|
+
const captured = performance.now();
|
|
599
|
+
const told = seat.answers.length ? [...snapshot.events, ...seat.answers.splice(0)] : snapshot.events;
|
|
600
|
+
if (told.length) seat.events.push({
|
|
601
|
+
tick: snapshot.tick,
|
|
602
|
+
n: ++seat.eventsMade,
|
|
603
|
+
events: told
|
|
604
|
+
});
|
|
605
|
+
let over = seat.events.length - this.keptEvents;
|
|
606
|
+
while (over-- > 0 && seat.events[0].sentAt !== void 0) seat.events.shift();
|
|
607
|
+
const entities = quantizedList(snapshot.entities, this.roundEntities);
|
|
608
|
+
const quantized = performance.now();
|
|
609
|
+
const fog = fogMessageOf(session.fog());
|
|
610
|
+
const fogged = performance.now();
|
|
611
|
+
const frame = {
|
|
612
|
+
snapshot: {
|
|
613
|
+
...snapshot,
|
|
614
|
+
entities,
|
|
615
|
+
events: []
|
|
616
|
+
},
|
|
617
|
+
fog,
|
|
618
|
+
camps: session.camps().map((camp) => ({ ...camp })),
|
|
619
|
+
team: session.team
|
|
620
|
+
};
|
|
621
|
+
const now = performance.now();
|
|
622
|
+
const overdue = seat.roundTrip * 1.5 + this.sendEvery * 1e3;
|
|
623
|
+
const events = seat.events.filter((sent) => sent.sentAt === void 0 || now - sent.sentAt > overdue);
|
|
624
|
+
for (const sent of events) sent.sentAt = now;
|
|
625
|
+
const base = seat.frames.get(seat.acked);
|
|
626
|
+
const delta = {
|
|
627
|
+
...diff(base, frame, this.world.registry),
|
|
628
|
+
events: events.map(({ tick, n, events }) => ({
|
|
629
|
+
tick,
|
|
630
|
+
n,
|
|
631
|
+
events
|
|
632
|
+
}))
|
|
633
|
+
};
|
|
634
|
+
if (!base) seat.whole = {
|
|
635
|
+
tick: frame.snapshot.tick,
|
|
636
|
+
at: now
|
|
637
|
+
};
|
|
638
|
+
parts.capture += captured - began;
|
|
639
|
+
parts.quantize += quantized - captured;
|
|
640
|
+
parts.fog += fogged - quantized;
|
|
641
|
+
parts.diff += performance.now() - fogged;
|
|
642
|
+
this.keep(seat, frame);
|
|
643
|
+
if (seat.inOrder) {
|
|
644
|
+
seat.acked = frame.snapshot.tick;
|
|
645
|
+
seat.whole = void 0;
|
|
646
|
+
for (const kept of [...seat.frames.keys()]) if (kept < seat.acked) this.forget(seat, kept);
|
|
647
|
+
}
|
|
648
|
+
seat.sentAt.set(frame.snapshot.tick, now);
|
|
649
|
+
if (seat.sentAt.size > this.keptFrames) seat.sentAt.delete(seat.sentAt.keys().next().value);
|
|
650
|
+
return delta;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Keeps the frame a player was sent, to make the next delta against: the oldest let go past `keptFrames` of them or
|
|
654
|
+
* `keptBytes` together, the newest kept whatever it takes. A player who says nothing for long is sent everything
|
|
655
|
+
* again, once its base is no longer kept
|
|
656
|
+
*/
|
|
657
|
+
keep(seat, frame) {
|
|
658
|
+
const { tick } = frame.snapshot;
|
|
659
|
+
const bytes = frameBytes(frame);
|
|
660
|
+
seat.frames.set(tick, frame);
|
|
661
|
+
seat.frameSizes.set(tick, bytes);
|
|
662
|
+
seat.keptBytes += bytes;
|
|
663
|
+
while (seat.frames.size > 1 && (seat.frames.size > this.keptFrames || seat.keptBytes > this.keptBytes)) this.forget(seat, seat.frames.keys().next().value);
|
|
664
|
+
}
|
|
665
|
+
/** Lets go of a frame kept for a player */
|
|
666
|
+
forget(seat, tick) {
|
|
667
|
+
if (!seat.frames.delete(tick)) return;
|
|
668
|
+
seat.keptBytes -= seat.frameSizes.get(tick) ?? 0;
|
|
669
|
+
seat.frameSizes.delete(tick);
|
|
670
|
+
}
|
|
671
|
+
/** Runs the world by the clock, `rate` updates a second, until `stop` or `dispose` */
|
|
672
|
+
start(rate = this.world.tickRate) {
|
|
673
|
+
this.stop();
|
|
674
|
+
this.last = performance.now();
|
|
675
|
+
this.timer = setInterval(() => {
|
|
676
|
+
const now = performance.now();
|
|
677
|
+
const dt = Math.min((now - this.last) / 1e3, .25);
|
|
678
|
+
this.last = now;
|
|
679
|
+
try {
|
|
680
|
+
this.update(dt);
|
|
681
|
+
} catch (error) {
|
|
682
|
+
this.stop();
|
|
683
|
+
if (this.onError) this.onError(error);
|
|
684
|
+
else this.world.log.error("server", "stepping the game threw: it is stopped", { error: errorFields(error) });
|
|
685
|
+
}
|
|
686
|
+
}, 1e3 / rate);
|
|
687
|
+
}
|
|
688
|
+
/** Stops stepping the world by the clock; does nothing if it was not started */
|
|
689
|
+
stop() {
|
|
690
|
+
if (this.timer !== void 0) clearInterval(this.timer);
|
|
691
|
+
this.timer = void 0;
|
|
692
|
+
}
|
|
693
|
+
/** Whether it runs by the clock */
|
|
694
|
+
get running() {
|
|
695
|
+
return this.timer !== void 0;
|
|
696
|
+
}
|
|
697
|
+
/** Stops, and closes every connection. The world goes too, when it is the host's (`ownsWorld`) */
|
|
698
|
+
/**
|
|
699
|
+
* Stops, and lets every player go: their connections closed, unless `closeTransports` is false (a server that keeps
|
|
700
|
+
* them for its lobby, after the game)
|
|
701
|
+
*/
|
|
702
|
+
dispose(options = {}) {
|
|
703
|
+
this.stop();
|
|
704
|
+
this.stopFailed();
|
|
705
|
+
for (const seat of [...this.seats]) {
|
|
706
|
+
if (options.closeTransports !== false) seat.transport.close();
|
|
707
|
+
this.leave(seat);
|
|
708
|
+
}
|
|
709
|
+
if (this.ownsWorld) this.world.dispose();
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
/**
|
|
713
|
+
* What the player's side keeps of the map, to answer the screen without asking the host: a placement grid and the
|
|
714
|
+
* ways to walk of the map as it was made, with the structures the player has seen on them. What they have not seen
|
|
715
|
+
* is not there: a path preview never goes around a building in the dark, and gives it away
|
|
716
|
+
*/
|
|
717
|
+
var ClientWorld = class {
|
|
718
|
+
terrain;
|
|
719
|
+
registry;
|
|
720
|
+
nav;
|
|
721
|
+
air;
|
|
722
|
+
grid;
|
|
723
|
+
known = /* @__PURE__ */ new Map();
|
|
724
|
+
seen = /* @__PURE__ */ new Set();
|
|
725
|
+
constructor(terrain, registry, nav, air) {
|
|
726
|
+
this.terrain = terrain;
|
|
727
|
+
this.registry = registry;
|
|
728
|
+
this.nav = nav;
|
|
729
|
+
this.air = air;
|
|
730
|
+
this.grid = new BuildGrid(terrain);
|
|
731
|
+
}
|
|
732
|
+
/** Whether a resource of a kind that it knows of covers just this ground, with nothing it knows of standing on it */
|
|
733
|
+
resourceAt(kind, cellX, cellZ, w, d) {
|
|
734
|
+
let found = false;
|
|
735
|
+
for (const structure of this.known.values()) {
|
|
736
|
+
if (structure.cellX !== cellX || structure.cellZ !== cellZ || structure.w !== w || structure.d !== d) continue;
|
|
737
|
+
if (structure.resource !== kind) return false;
|
|
738
|
+
found = true;
|
|
739
|
+
}
|
|
740
|
+
return found;
|
|
741
|
+
}
|
|
742
|
+
/** Brings the structures up to date with a snapshot: those in it stand, those no longer in it are gone */
|
|
743
|
+
sync(snapshot) {
|
|
744
|
+
const { seen, known } = this;
|
|
745
|
+
seen.clear();
|
|
746
|
+
let changed = false;
|
|
747
|
+
for (const entity of snapshot.entities) {
|
|
748
|
+
if (!this.registry.units.has(entity.type)) continue;
|
|
749
|
+
if (!this.registry.unit(entity.type).footprint) continue;
|
|
750
|
+
seen.add(entity.id);
|
|
751
|
+
if (this.stand(entity)) changed = true;
|
|
752
|
+
}
|
|
753
|
+
const gone = [];
|
|
754
|
+
for (const id of known.keys()) if (!seen.has(id)) gone.push(id);
|
|
755
|
+
this.settle(gone, changed);
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Brings the structures up to date with a snapshot made on the one before it, from what came and went alone: the
|
|
759
|
+
* last `added` of its entities (`apply` puts those that came after the rest), and the ids of those `removed`. What
|
|
760
|
+
* `sync` does, without looking over every entity of the map
|
|
761
|
+
*/
|
|
762
|
+
change(snapshot, added, removed) {
|
|
763
|
+
const { entities } = snapshot;
|
|
764
|
+
let changed = false;
|
|
765
|
+
const gone = removed.filter((id) => this.known.has(id));
|
|
766
|
+
for (const id of gone) this.release(id);
|
|
767
|
+
for (let i = entities.length - added; i < entities.length; i++) {
|
|
768
|
+
const entity = entities[i];
|
|
769
|
+
if (!this.registry.units.has(entity.type) || !this.registry.unit(entity.type).footprint) continue;
|
|
770
|
+
if (this.stand(entity)) changed = true;
|
|
771
|
+
}
|
|
772
|
+
this.settle([], changed || gone.length > 0, gone.length > 0);
|
|
773
|
+
}
|
|
774
|
+
/** Puts a structure on the map, where it is not on it yet: whether it was put */
|
|
775
|
+
stand(entity) {
|
|
776
|
+
const { known, grid, nav } = this;
|
|
777
|
+
if (known.has(entity.id)) return false;
|
|
778
|
+
const def = this.registry.unit(entity.type);
|
|
779
|
+
const site = siteOf(def, entity);
|
|
780
|
+
const { w, d } = footprintOf(def);
|
|
781
|
+
known.set(entity.id, {
|
|
782
|
+
cellX: site.cellX,
|
|
783
|
+
cellZ: site.cellZ,
|
|
784
|
+
w,
|
|
785
|
+
d,
|
|
786
|
+
...def.resource && { resource: def.resource.id }
|
|
787
|
+
});
|
|
788
|
+
grid.occupy(site.cellX, site.cellZ, w, d, entity.id);
|
|
789
|
+
nav.addObstacle(entity.id, site);
|
|
790
|
+
return true;
|
|
791
|
+
}
|
|
792
|
+
/** Takes a structure off the map */
|
|
793
|
+
release(id) {
|
|
794
|
+
const structure = this.known.get(id);
|
|
795
|
+
this.known.delete(id);
|
|
796
|
+
this.grid.release(structure.cellX, structure.cellZ, structure.w, structure.d, id);
|
|
797
|
+
this.nav.removeObstacle(id);
|
|
798
|
+
}
|
|
799
|
+
/** Takes off those gone, gives the ground back to what stood on them, and has the navmesh take in what changed */
|
|
800
|
+
settle(gone, changed, released = false) {
|
|
801
|
+
for (const id of gone) this.release(id);
|
|
802
|
+
released ||= gone.length > 0;
|
|
803
|
+
if (released) for (const [id, structure] of this.known) this.grid.occupy(structure.cellX, structure.cellZ, structure.w, structure.d, id);
|
|
804
|
+
if (changed || released) this.nav.update(0);
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
/**
|
|
808
|
+
* The host said no, with why: to come back to a game that is over, or a player let go. Trying again does not help
|
|
809
|
+
*
|
|
810
|
+
* @beta
|
|
811
|
+
*/
|
|
812
|
+
var SessionRefused = class extends Error {
|
|
813
|
+
reason;
|
|
814
|
+
constructor(reason) {
|
|
815
|
+
super(reason);
|
|
816
|
+
this.reason = reason;
|
|
817
|
+
this.name = "SessionRefused";
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
/**
|
|
821
|
+
* A player's seat at a world that runs elsewhere, over a transport (see `connect`). It rebuilds what the host sends
|
|
822
|
+
* as changes (`Delta`) into whole snapshots, keeping the objects of what did not change, says which ticks it got, and
|
|
823
|
+
* answers the screen's questions from those and its own copy of the map (`ClientWorld`). No debug access, and no
|
|
824
|
+
* checkpoints: those belong to a simulation in the page
|
|
825
|
+
*
|
|
826
|
+
* @beta
|
|
827
|
+
*/
|
|
828
|
+
var RemoteSession = class {
|
|
829
|
+
transport;
|
|
830
|
+
map;
|
|
831
|
+
limits;
|
|
832
|
+
/**
|
|
833
|
+
* A seat over a wire can none of what a game in this page can (`SessionCan`), but the debug tools' view of the world
|
|
834
|
+
* for a player the host lets use them (`inspect`, said over the wire)
|
|
835
|
+
*/
|
|
836
|
+
can;
|
|
837
|
+
/** The debug tools' view of the world, for a player the host lets use them (the welcome's `debug`) */
|
|
838
|
+
get debugView() {
|
|
839
|
+
return this.remoteDebug;
|
|
840
|
+
}
|
|
841
|
+
remoteDebug;
|
|
842
|
+
/** The player this seat plays, as the host welcomed it */
|
|
843
|
+
playerId;
|
|
844
|
+
/** The team of that player; updated when a snapshot says it changed */
|
|
845
|
+
team;
|
|
846
|
+
/** The ground, built from the welcome and the parts the host sent */
|
|
847
|
+
terrain;
|
|
848
|
+
/** The map's look (sky, mist, light) from the welcome, when it has one */
|
|
849
|
+
environment;
|
|
850
|
+
/** The map's scenery objects from the welcome */
|
|
851
|
+
doodads;
|
|
852
|
+
/** The map's named regions from the welcome */
|
|
853
|
+
regions;
|
|
854
|
+
/** The map's named camera positions from the welcome */
|
|
855
|
+
cameras;
|
|
856
|
+
/** The map's named paths from the welcome */
|
|
857
|
+
paths;
|
|
858
|
+
/** The definitions of the game, from the rules the player connected with */
|
|
859
|
+
registry;
|
|
860
|
+
/** Simulation ticks per second the host runs at, from the welcome */
|
|
861
|
+
tickRate;
|
|
862
|
+
/** How far, 0..1, the shown tick is between the last two snapshots, for blending the drawing */
|
|
863
|
+
alpha = 1;
|
|
864
|
+
/** What the player knows by tick, for the deltas to come: from the base of the last one on */
|
|
865
|
+
frames = /* @__PURE__ */ new Map();
|
|
866
|
+
latest;
|
|
867
|
+
incoming = [];
|
|
868
|
+
/** What the host's definitions were changed to while it ran (`world.redefine`), as this screen heard it */
|
|
869
|
+
redefined = {};
|
|
870
|
+
/** The number of the last batch of events that went to the screen (`Delta.events`): those sent again are not applied twice */
|
|
871
|
+
eventsUpTo = -1;
|
|
872
|
+
fogState;
|
|
873
|
+
closed = false;
|
|
874
|
+
/** The tick the screen shows, between the last two snapshots given out */
|
|
875
|
+
renderTick;
|
|
876
|
+
/** The tick of the snapshot given out last, and of the one before it */
|
|
877
|
+
shownTick;
|
|
878
|
+
shownBefore;
|
|
879
|
+
/** Ticks between two snapshots, evened out */
|
|
880
|
+
interval = 1;
|
|
881
|
+
/** How far off the time snapshots arrive at is from the ticks between them, in seconds, evened out */
|
|
882
|
+
jitter = 0;
|
|
883
|
+
/** Seconds between two snapshots' arrivals, evened out: a seek's come as often as any, their ticks far apart */
|
|
884
|
+
gap = 0;
|
|
885
|
+
/** When the last snapshot arrived, and its tick */
|
|
886
|
+
arrival;
|
|
887
|
+
constructor(transport, welcome, registry, map, limits = {}) {
|
|
888
|
+
this.transport = transport;
|
|
889
|
+
this.map = map;
|
|
890
|
+
this.limits = limits;
|
|
891
|
+
this.playerId = welcome.player;
|
|
892
|
+
this.tickRate = welcome.tickRate;
|
|
893
|
+
this.observer = welcome.observer ?? false;
|
|
894
|
+
this.remoteDebug = welcome.debug ? new RemoteDebug((ask) => this.tell({
|
|
895
|
+
type: "debug",
|
|
896
|
+
...ask
|
|
897
|
+
})) : void 0;
|
|
898
|
+
this.can = this.remoteDebug ? { inspect: this.remoteDebug } : {};
|
|
899
|
+
this.registry = registry;
|
|
900
|
+
this.terrain = map.terrain;
|
|
901
|
+
this.environment = welcome.environment;
|
|
902
|
+
this.doodads = welcome.doodads ?? [];
|
|
903
|
+
this.regions = welcome.regions ?? [];
|
|
904
|
+
this.cameras = welcome.cameras ?? [];
|
|
905
|
+
this.paths = welcome.paths ?? [];
|
|
906
|
+
restoreRedefinitions(registry, welcome.redefinitions);
|
|
907
|
+
this.heardRedefined(welcome.redefinitions);
|
|
908
|
+
const cells = this.terrain.width * this.terrain.depth;
|
|
909
|
+
this.fogState = {
|
|
910
|
+
width: this.terrain.width,
|
|
911
|
+
depth: this.terrain.depth,
|
|
912
|
+
version: 0,
|
|
913
|
+
revealed: false,
|
|
914
|
+
visible: new Uint8Array(cells),
|
|
915
|
+
explored: new Uint8Array(cells)
|
|
916
|
+
};
|
|
917
|
+
const first = this.take(welcome.first);
|
|
918
|
+
this.latest = first;
|
|
919
|
+
this.team = first.team;
|
|
920
|
+
this.incoming.length = 0;
|
|
921
|
+
this.shownTick = this.shownBefore = first.snapshot.tick;
|
|
922
|
+
this.attach(transport);
|
|
923
|
+
}
|
|
924
|
+
/** Talks through a transport from now on: what comes in is taken, and its closing is a drop */
|
|
925
|
+
attach(transport) {
|
|
926
|
+
this.transport = transport;
|
|
927
|
+
this.closed = false;
|
|
928
|
+
this.leaving = false;
|
|
929
|
+
transport.onMessage((text) => {
|
|
930
|
+
const message = readHostMessage(text);
|
|
931
|
+
if (message) this.receive(message);
|
|
932
|
+
});
|
|
933
|
+
transport.onClose(() => {
|
|
934
|
+
if (this.transport !== transport) return;
|
|
935
|
+
this.closed = true;
|
|
936
|
+
clearInterval(this.pinger);
|
|
937
|
+
if (!this.leaving) this.onDisconnect?.();
|
|
938
|
+
});
|
|
939
|
+
this.sendPing();
|
|
940
|
+
clearInterval(this.pinger);
|
|
941
|
+
this.pinger = setInterval(() => this.sendPing(), this.limits.pingEvery ?? PING_EVERY);
|
|
942
|
+
}
|
|
943
|
+
/** Whether the host has gone */
|
|
944
|
+
get disconnected() {
|
|
945
|
+
return this.closed;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Told when the connection drops, not by this side's leaving (`dispose`): to try again (`resume`), or to say the
|
|
949
|
+
* game is lost
|
|
950
|
+
*/
|
|
951
|
+
onDisconnect;
|
|
952
|
+
/** This side closed it: not a drop */
|
|
953
|
+
leaving = false;
|
|
954
|
+
/**
|
|
955
|
+
* Goes on with the same game over a new connection, once the host has welcomed it there again (a server does, to who
|
|
956
|
+
* comes back with their seat's token): everything known sent again, and the screen catches up. What the host says
|
|
957
|
+
* besides the game before the welcome goes to `onServer`; a `refused` fails it with the reason, as does a connection
|
|
958
|
+
* that closes first, or a host that runs another game
|
|
959
|
+
*/
|
|
960
|
+
async resume(transport) {
|
|
961
|
+
const welcome = await new Promise((resolve, reject) => {
|
|
962
|
+
transport.onClose(() => reject(/* @__PURE__ */ new Error("The host closed the connection before it took the player back")));
|
|
963
|
+
transport.onMessage((text) => {
|
|
964
|
+
const message = readHostMessage(text);
|
|
965
|
+
if (!message) return;
|
|
966
|
+
if (message.type === "welcome") resolve(message);
|
|
967
|
+
else if (message.type === "refused") reject(new SessionRefused(String(message.reason ?? "refused")));
|
|
968
|
+
else if (isServerMessage(message)) this.onServer?.(message);
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
const problem = welcomeProblem(welcome);
|
|
972
|
+
if (problem) {
|
|
973
|
+
transport.close();
|
|
974
|
+
throw new Error(problem);
|
|
975
|
+
}
|
|
976
|
+
if (welcome.game !== this.registry.game || welcome.hash !== this.registry.dataHash || welcome.player !== this.playerId) {
|
|
977
|
+
transport.close();
|
|
978
|
+
throw new Error("The host took the player back to another game");
|
|
979
|
+
}
|
|
980
|
+
restoreRedefinitions(this.registry, welcome.redefinitions);
|
|
981
|
+
this.heardRedefined(welcome.redefinitions);
|
|
982
|
+
this.eventsUpTo = -1;
|
|
983
|
+
this.attach(transport);
|
|
984
|
+
this.frames.clear();
|
|
985
|
+
this.incoming.length = 0;
|
|
986
|
+
this.arrival = void 0;
|
|
987
|
+
this.interval = 1;
|
|
988
|
+
this.jitter = 0;
|
|
989
|
+
this.gap = 0;
|
|
990
|
+
if (welcome.first.tick < this.shownTick) {
|
|
991
|
+
this.shownTick = this.shownBefore = welcome.first.tick;
|
|
992
|
+
this.renderTick = void 0;
|
|
993
|
+
this.eventsUpTo = welcome.first.tick - 1;
|
|
994
|
+
}
|
|
995
|
+
this.take(welcome.first);
|
|
996
|
+
this.remoteDebug?.askAgain();
|
|
997
|
+
}
|
|
998
|
+
/** Keeps what the host's definitions were changed to, over what was kept before */
|
|
999
|
+
heardRedefined(data) {
|
|
1000
|
+
for (const [kind, byId] of Object.entries(data ?? {})) for (const [id, values] of Object.entries(byId)) {
|
|
1001
|
+
const kept = this.redefined[kind] ??= {};
|
|
1002
|
+
kept[id] = mergeValues(kept[id] ?? {}, values);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Takes a new version of the game into this screen's registry (hot reload, a game in a worker): its definitions as
|
|
1007
|
+
* the new version has them, what the host changed of them while it ran over them again
|
|
1008
|
+
*/
|
|
1009
|
+
takeGame(game) {
|
|
1010
|
+
this.registry.takeFrom(createRegistry(game));
|
|
1011
|
+
restoreRedefinitions(this.registry, this.redefined);
|
|
1012
|
+
}
|
|
1013
|
+
/** Takes in a message of the host's: what the transport hands it, and what came in while the session was made */
|
|
1014
|
+
receive(message) {
|
|
1015
|
+
if (message.type === "delta") this.take(message);
|
|
1016
|
+
else if (message.type === "debug") this.remoteDebug?.take(message);
|
|
1017
|
+
else if (message.type === "pong") {
|
|
1018
|
+
const took = performance.now() - message.at;
|
|
1019
|
+
this.ping = this.ping === void 0 ? took : this.ping + (took - this.ping) * .3;
|
|
1020
|
+
} else if (message.type === "failed") {
|
|
1021
|
+
if (typeof message.message === "string") this.failure = { message: message.message };
|
|
1022
|
+
} else if (isServerMessage(message)) {
|
|
1023
|
+
if (message.type === "paused") {
|
|
1024
|
+
this.pause = message.paused === true ? {
|
|
1025
|
+
...typeof message.player === "number" && { player: message.player },
|
|
1026
|
+
...typeof message.seconds === "number" && { until: Date.now() + message.seconds * 1e3 }
|
|
1027
|
+
} : null;
|
|
1028
|
+
this.onPause?.(this.pause);
|
|
1029
|
+
}
|
|
1030
|
+
this.onServer?.(message);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* The game paused by the server, for everyone: by a player (`player`, until a time at most, in `Date.now()`'s
|
|
1035
|
+
* milliseconds) or an admin (neither). Null while it goes on
|
|
1036
|
+
*/
|
|
1037
|
+
pause = null;
|
|
1038
|
+
/** Told each time the server pauses the game or lets it go on */
|
|
1039
|
+
onPause;
|
|
1040
|
+
/** Says something to the server beside the game: a lobby's message (`{ type: 'resume' }`) */
|
|
1041
|
+
tell(message) {
|
|
1042
|
+
if (!this.closed) this.transport.send(JSON.stringify(message));
|
|
1043
|
+
}
|
|
1044
|
+
/** Why the game ended on an error in its rules, once the host said so */
|
|
1045
|
+
failure = null;
|
|
1046
|
+
/** Milliseconds a message takes to the host and back, evened out: measured every couple of seconds */
|
|
1047
|
+
ping;
|
|
1048
|
+
/** Whether the player only watches, and sees everything */
|
|
1049
|
+
observer;
|
|
1050
|
+
pinger;
|
|
1051
|
+
/** Asks the host to answer at once, to measure the time there and back */
|
|
1052
|
+
sendPing() {
|
|
1053
|
+
if (!this.closed) this.transport.send(JSON.stringify({
|
|
1054
|
+
type: "ping",
|
|
1055
|
+
at: performance.now()
|
|
1056
|
+
}));
|
|
1057
|
+
}
|
|
1058
|
+
/** Told of what the host says besides the game: a server's messages (see `ConnectOptions.onServer`) */
|
|
1059
|
+
onServer;
|
|
1060
|
+
/** Rebuilds the frame a delta makes, says it got it, and has its snapshot wait for the screen */
|
|
1061
|
+
take(delta) {
|
|
1062
|
+
const base = delta.base === null ? void 0 : this.frames.get(delta.base);
|
|
1063
|
+
if (delta.base !== null && !base) return void 0;
|
|
1064
|
+
const frame = apply(base, delta, this.latest);
|
|
1065
|
+
const { snapshot } = frame;
|
|
1066
|
+
this.frames.set(snapshot.tick, frame);
|
|
1067
|
+
for (const tick of this.frames.keys()) if (delta.base !== null && tick < delta.base) this.frames.delete(tick);
|
|
1068
|
+
if (delta.rest.fog) this.takeFog(delta.rest.fog);
|
|
1069
|
+
if (delta.rest.team !== void 0) this.team = delta.rest.team;
|
|
1070
|
+
const events = [];
|
|
1071
|
+
for (const sent of delta.events) {
|
|
1072
|
+
if (sent.n <= this.eventsUpTo) continue;
|
|
1073
|
+
events.push(...sent.events);
|
|
1074
|
+
this.eventsUpTo = sent.n;
|
|
1075
|
+
for (const event of sent.events) {
|
|
1076
|
+
if (event.type === "redefined") {
|
|
1077
|
+
redefineIn(this.registry, event.kind, event.definition, event.values);
|
|
1078
|
+
this.heardRedefined({ [event.kind]: { [event.definition]: event.values } });
|
|
1079
|
+
}
|
|
1080
|
+
if (event.type === "ground" && !terrainPatchProblem(this.terrain, event.patch)) applyTerrainPatch(this.terrain, event.patch);
|
|
1081
|
+
if (event.type !== "cells") continue;
|
|
1082
|
+
const layer = this.terrain.cellLayer(event.layer);
|
|
1083
|
+
for (const c of event.at) if (c >= 0 && c < layer.length) layer[c] = event.value;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
this.incoming.push({
|
|
1087
|
+
...snapshot,
|
|
1088
|
+
events
|
|
1089
|
+
});
|
|
1090
|
+
if (this.incoming.length > (this.limits.keptSnapshots ?? KEPT_SNAPSHOTS)) this.fold();
|
|
1091
|
+
this.arrived(snapshot.tick);
|
|
1092
|
+
if (base && base === this.latest) this.map.change(snapshot, delta.added.length, delta.removed);
|
|
1093
|
+
else this.map.sync(snapshot);
|
|
1094
|
+
this.latest = frame;
|
|
1095
|
+
if (!this.closed) this.transport.send(JSON.stringify({
|
|
1096
|
+
type: "ack",
|
|
1097
|
+
tick: snapshot.tick,
|
|
1098
|
+
events: this.eventsUpTo
|
|
1099
|
+
}));
|
|
1100
|
+
return frame;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* The oldest snapshot waiting let go, its events put before the next one's: the screen, when it asks again, jumps
|
|
1104
|
+
* to the newest anyway. The state is in every snapshot; the events (deaths, sounds, lines said) are what would be lost
|
|
1105
|
+
*/
|
|
1106
|
+
fold() {
|
|
1107
|
+
const oldest = this.incoming.shift();
|
|
1108
|
+
if (!oldest.events.length) return;
|
|
1109
|
+
const next = this.incoming[0];
|
|
1110
|
+
const events = oldest.events.concat(next.events);
|
|
1111
|
+
const mostEvents = this.limits.keptScreenEvents ?? KEPT_SCREEN_EVENTS;
|
|
1112
|
+
this.incoming[0] = {
|
|
1113
|
+
...next,
|
|
1114
|
+
events: events.length > mostEvents ? events.slice(-mostEvents) : events
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Snapshots come in and not yet given to the screen (`update`): a few normally, never more than a few seconds of
|
|
1119
|
+
* them, however long the screen does not ask
|
|
1120
|
+
*
|
|
1121
|
+
* @beta
|
|
1122
|
+
*/
|
|
1123
|
+
get waiting() {
|
|
1124
|
+
return this.incoming.length;
|
|
1125
|
+
}
|
|
1126
|
+
takeFog(message) {
|
|
1127
|
+
const fog = this.fogState;
|
|
1128
|
+
fog.revealed = message.revealed;
|
|
1129
|
+
fromRuns(message.visible, fog.visible);
|
|
1130
|
+
fromRuns(message.explored, fog.explored);
|
|
1131
|
+
fog.version++;
|
|
1132
|
+
}
|
|
1133
|
+
/** The newest snapshot the host sent */
|
|
1134
|
+
snapshot() {
|
|
1135
|
+
return this.latest.snapshot;
|
|
1136
|
+
}
|
|
1137
|
+
/** The tick the screen is shown at, between two snapshots: behind the newest by `delay` */
|
|
1138
|
+
get shownAt() {
|
|
1139
|
+
return this.renderTick ?? this.shownTick;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* The ticks between two snapshots, and the jitter the interpolation makes room for, as it has them evened out. A
|
|
1143
|
+
* test may set the jitter, as a late line would have
|
|
1144
|
+
*
|
|
1145
|
+
* @internal
|
|
1146
|
+
*/
|
|
1147
|
+
get pacing() {
|
|
1148
|
+
return {
|
|
1149
|
+
interval: this.interval,
|
|
1150
|
+
jitter: this.jitter
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
/** @internal */
|
|
1154
|
+
set pacing(pacing) {
|
|
1155
|
+
this.jitter = pacing.jitter;
|
|
1156
|
+
}
|
|
1157
|
+
/** How far behind the newest snapshot the screen is shown, in ticks: about two snapshots, and room for the jitter */
|
|
1158
|
+
get delay() {
|
|
1159
|
+
return this.interval * 2 + Math.min(this.jitter, this.limits.maxJitter ?? .2) * this.tickRate * 2;
|
|
1160
|
+
}
|
|
1161
|
+
/** Takes note of when a snapshot arrived, for how far apart they come and how unevenly */
|
|
1162
|
+
arrived(tick) {
|
|
1163
|
+
const now = performance.now();
|
|
1164
|
+
const last = this.arrival;
|
|
1165
|
+
this.arrival = {
|
|
1166
|
+
at: now,
|
|
1167
|
+
tick
|
|
1168
|
+
};
|
|
1169
|
+
if (!last || tick <= last.tick) return;
|
|
1170
|
+
const ticks = tick - last.tick;
|
|
1171
|
+
this.interval += (ticks - this.interval) * .1;
|
|
1172
|
+
this.gap += ((now - last.at) / 1e3 - this.gap) * .1;
|
|
1173
|
+
const off = Math.abs((now - last.at) / 1e3 - ticks / this.tickRate);
|
|
1174
|
+
this.jitter += (off - this.jitter) * .1;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Lets the screen's clock go on by `dt`, and gives out the snapshots it has come to, oldest first: the screen draws
|
|
1178
|
+
* between the last two (`alpha`). The time is the host's: the clock follows the snapshots, not the other way around
|
|
1179
|
+
*/
|
|
1180
|
+
update(dt) {
|
|
1181
|
+
const newest = this.incoming.length ? this.incoming[this.incoming.length - 1].tick : this.shownTick;
|
|
1182
|
+
const target = !!this.arrival && performance.now() - this.arrival.at > Math.max(QUIET, this.gap * 3) * 1e3 ? newest : newest - this.delay;
|
|
1183
|
+
if (this.renderTick === void 0 || target - this.renderTick > this.tickRate) this.renderTick = target;
|
|
1184
|
+
else {
|
|
1185
|
+
const rate = 1 + Math.max(-.1, Math.min((target - this.renderTick) * .05, .1));
|
|
1186
|
+
this.renderTick += dt * this.tickRate * rate;
|
|
1187
|
+
}
|
|
1188
|
+
this.renderTick = Math.min(this.renderTick, newest);
|
|
1189
|
+
const out = [];
|
|
1190
|
+
while (this.incoming.length && this.renderTick >= this.shownTick) {
|
|
1191
|
+
const next = this.incoming.shift();
|
|
1192
|
+
this.shownBefore = this.shownTick;
|
|
1193
|
+
this.shownTick = next.tick;
|
|
1194
|
+
out.push(next);
|
|
1195
|
+
}
|
|
1196
|
+
const span = this.shownTick - this.shownBefore;
|
|
1197
|
+
this.alpha = span > 0 ? Math.max(0, Math.min((this.renderTick - this.shownBefore) / span, 1)) : 1;
|
|
1198
|
+
return out;
|
|
1199
|
+
}
|
|
1200
|
+
/** Sends a command to the host and returns the number it was given, which the host's answer names; does nothing once closed */
|
|
1201
|
+
send(command) {
|
|
1202
|
+
const seq = ++this.lastSeq;
|
|
1203
|
+
if (!this.closed) this.transport.send(JSON.stringify({
|
|
1204
|
+
type: "command",
|
|
1205
|
+
seq,
|
|
1206
|
+
command
|
|
1207
|
+
}));
|
|
1208
|
+
return seq;
|
|
1209
|
+
}
|
|
1210
|
+
/** The number of the last command sent */
|
|
1211
|
+
lastSeq = 0;
|
|
1212
|
+
/** The team's fog as built from what the host sent */
|
|
1213
|
+
fog() {
|
|
1214
|
+
return this.fogState;
|
|
1215
|
+
}
|
|
1216
|
+
/** The creep camps as the newest frame has them */
|
|
1217
|
+
camps() {
|
|
1218
|
+
return this.latest.camps;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Whether the player could build this here, as far as they can tell: on their copy of the map, with the structures
|
|
1222
|
+
* they know, on ground they have explored. The game's own say (the `canPlace` hook) is the host's: it may still refuse
|
|
1223
|
+
*/
|
|
1224
|
+
canPlace(unitType, point) {
|
|
1225
|
+
if (!this.registry.units.has(unitType)) return false;
|
|
1226
|
+
const def = this.registry.unit(unitType);
|
|
1227
|
+
const site = siteOf(def, point);
|
|
1228
|
+
const { w, d } = footprintOf(def);
|
|
1229
|
+
if (def.builtOn !== void 0 ? !this.map.resourceAt(def.builtOn, site.cellX, site.cellZ, w, d) : !this.map.grid.canPlace(site.cellX, site.cellZ, w, d)) return false;
|
|
1230
|
+
const fog = this.fogState;
|
|
1231
|
+
if (fog.revealed) return true;
|
|
1232
|
+
const explored = (x, z) => fog.explored[Math.floor(z) * fog.width + Math.floor(x)] === 1;
|
|
1233
|
+
return explored(site.x - site.hw + .5, site.z - site.hd + .5) && explored(site.x + site.hw - .5, site.z - site.hd + .5) && explored(site.x - site.hw + .5, site.z + site.hd - .5) && explored(site.x + site.hw - .5, site.z + site.hd - .5);
|
|
1234
|
+
}
|
|
1235
|
+
/** Which cells of a building's ground are in the way, as `canPlace` sees them: on the player's copy of the map, explored */
|
|
1236
|
+
placeCells(unitType, point) {
|
|
1237
|
+
if (!this.registry.units.has(unitType)) return void 0;
|
|
1238
|
+
const fog = this.fogState;
|
|
1239
|
+
return placementCells(this.registry.unit(unitType), point, (x, z) => this.map.grid.canPlace(x, z, 1, 1), fog.revealed ? void 0 : (x, z) => fog.explored[z * fog.width + x] === 1);
|
|
1240
|
+
}
|
|
1241
|
+
/** The way a unit would walk, on the player's copy of the map: around the structures they know, and no others */
|
|
1242
|
+
/** Where a group would stand sent to a point in a formation, `rows` when none is named: the rules are the host's (`GameSession.formationPlaces`) */
|
|
1243
|
+
formationPlaces(units, point, facing, formation = "rows") {
|
|
1244
|
+
return formationPreview(this.registry, units, point, this.map, formation, facing);
|
|
1245
|
+
}
|
|
1246
|
+
/** A path over the ground the player has, on the air layer when `flying`; null when there is none */
|
|
1247
|
+
findPath(from, to, flying = false) {
|
|
1248
|
+
return (flying ? this.map.air : this.map.nav).findPath(from, to);
|
|
1249
|
+
}
|
|
1250
|
+
/** Stops pinging, closes the connection and frees the navigation made from the ground */
|
|
1251
|
+
dispose() {
|
|
1252
|
+
clearInterval(this.pinger);
|
|
1253
|
+
this.leaving = true;
|
|
1254
|
+
if (!this.closed) this.transport.close();
|
|
1255
|
+
this.closed = true;
|
|
1256
|
+
this.map.nav.dispose();
|
|
1257
|
+
this.map.air.dispose();
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
/** How much of the ground goes in one message, for a player who does not have its map: well under what a connection holds */
|
|
1261
|
+
var GROUND_PART = 262144;
|
|
1262
|
+
/**
|
|
1263
|
+
* The ground of a welcome: all of it, or the map it started as among the player's (S36), the changes put on it. A
|
|
1264
|
+
* player without that map asks the host for it, a part at a time, and puts the changes on what it is sent
|
|
1265
|
+
*/
|
|
1266
|
+
async function groundOfWelcome(transport, ground, maps, game, early, dropped) {
|
|
1267
|
+
try {
|
|
1268
|
+
return groundOf(ground, maps, game).terrain;
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
if (!(error instanceof MissingGround) || !isGroundChanges(ground)) throw error;
|
|
1271
|
+
}
|
|
1272
|
+
const whole = await new Promise((resolve, reject) => {
|
|
1273
|
+
const parts = [];
|
|
1274
|
+
transport.onClose(() => {
|
|
1275
|
+
dropped();
|
|
1276
|
+
reject(/* @__PURE__ */ new Error("The host closed the connection while the ground was sent"));
|
|
1277
|
+
});
|
|
1278
|
+
transport.onMessage((text) => {
|
|
1279
|
+
const message = readHostMessage(text);
|
|
1280
|
+
if (message?.type !== "ground") {
|
|
1281
|
+
early.push(text);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
parts[message.part] = message.text;
|
|
1285
|
+
if (message.part + 1 < message.parts) transport.send(JSON.stringify({
|
|
1286
|
+
type: "ground",
|
|
1287
|
+
part: message.part + 1
|
|
1288
|
+
}));
|
|
1289
|
+
else {
|
|
1290
|
+
transport.onMessage((later) => early.push(later));
|
|
1291
|
+
resolve(JSON.parse(parts.join("")));
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
transport.send(JSON.stringify({
|
|
1295
|
+
type: "ground",
|
|
1296
|
+
part: 0
|
|
1297
|
+
}));
|
|
1298
|
+
});
|
|
1299
|
+
return applyGroundChanges(loadTerrain(whole), ground);
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* What is wrong with a welcome, before anything of it is used: another version of the wire (`PROTOCOL_VERSION`), or a
|
|
1303
|
+
* shape that is not one. What comes after it is the host's word, trusted as the welcome said it would be
|
|
1304
|
+
*/
|
|
1305
|
+
function welcomeProblem(welcome) {
|
|
1306
|
+
if (welcome.protocol !== 13) return protocolMismatch(welcome.protocol);
|
|
1307
|
+
return Number.isInteger(welcome.player) && isFiniteNumber(welcome.tickRate) && welcome.tickRate > 0 && typeof welcome.game === "string" && typeof welcome.hash === "string" && typeof welcome.terrain === "object" && welcome.terrain !== null && typeof welcome.first === "object" && welcome.first !== null ? void 0 : "The host sent a welcome this player cannot read";
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Connects to a host over a transport (the player's end of it), and waits for the welcome: what the session is
|
|
1311
|
+
* made of. Fails when the host runs another version of the game, or of the engine's wire (`PROTOCOL_VERSION`), and
|
|
1312
|
+
* when the connection closes before the session is made: while the map is made from the welcome too
|
|
1313
|
+
*
|
|
1314
|
+
* @public
|
|
1315
|
+
*/
|
|
1316
|
+
function connect(transport, options) {
|
|
1317
|
+
return connectAs(transport, options, (...made) => new RemoteSession(...made));
|
|
1318
|
+
}
|
|
1319
|
+
/** `connect`, for a session of a kind of its own: `make` is given what a `RemoteSession` is made of */
|
|
1320
|
+
async function connectAs(transport, options, make) {
|
|
1321
|
+
let dropped = false;
|
|
1322
|
+
const drop = () => {
|
|
1323
|
+
dropped = true;
|
|
1324
|
+
};
|
|
1325
|
+
const welcome = await new Promise((resolve, reject) => {
|
|
1326
|
+
transport.onClose(() => {
|
|
1327
|
+
drop();
|
|
1328
|
+
reject(/* @__PURE__ */ new Error("The host closed the connection before the game started"));
|
|
1329
|
+
});
|
|
1330
|
+
transport.onMessage((text) => {
|
|
1331
|
+
const message = readHostMessage(text);
|
|
1332
|
+
if (!message) return;
|
|
1333
|
+
if (message.type === "welcome") resolve(message);
|
|
1334
|
+
else if (isServerMessage(message)) options.onServer?.(message);
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
const problem = welcomeProblem(welcome);
|
|
1338
|
+
if (problem) {
|
|
1339
|
+
transport.close();
|
|
1340
|
+
throw new Error(problem);
|
|
1341
|
+
}
|
|
1342
|
+
const game = "id" in options.rules && typeof options.rules.id === "string" ? options.rules : options.rules[welcome.game];
|
|
1343
|
+
const own = "id" in options.rules && typeof options.rules.id === "string" ? options.rules.id : void 0;
|
|
1344
|
+
const others = otherPacks(welcome.game, game?.id ?? own);
|
|
1345
|
+
if (!game) {
|
|
1346
|
+
transport.close();
|
|
1347
|
+
throw new Error(others ?? `The host runs ${welcome.game}, which this player does not have`);
|
|
1348
|
+
}
|
|
1349
|
+
const registry = createRegistry(game);
|
|
1350
|
+
if (welcome.game !== registry.game || welcome.hash !== registry.dataHash) {
|
|
1351
|
+
transport.close();
|
|
1352
|
+
throw new Error(others ?? `The host runs ${welcome.game} (${welcome.hash}), this is ${registry.game} (${registry.dataHash}): not the same game`);
|
|
1353
|
+
}
|
|
1354
|
+
const early = [];
|
|
1355
|
+
transport.onMessage((text) => early.push(text));
|
|
1356
|
+
transport.onClose(drop);
|
|
1357
|
+
const lost = "The host closed the connection while the map was made";
|
|
1358
|
+
if (dropped) throw new Error(lost);
|
|
1359
|
+
const terrain = await groundOfWelcome(transport, welcome.terrain, options.maps ?? game.maps, game, early, drop);
|
|
1360
|
+
transport.onClose(drop);
|
|
1361
|
+
const speedAt = hasGroundSpeeds(registry) ? (x, z) => navigationSpeedOn(registry, terrain, x, z) : void 0;
|
|
1362
|
+
const nav = await RecastNavigation.create(terrain, {
|
|
1363
|
+
agentRadius: .5,
|
|
1364
|
+
...speedAt && { speedAt },
|
|
1365
|
+
...options.navigation
|
|
1366
|
+
});
|
|
1367
|
+
if (dropped) {
|
|
1368
|
+
nav.dispose();
|
|
1369
|
+
throw new Error(lost);
|
|
1370
|
+
}
|
|
1371
|
+
const session = make(transport, welcome, registry, new ClientWorld(terrain, registry, nav, new AirNavigation(terrain.width, terrain.depth)), options.limits);
|
|
1372
|
+
session.onServer = options.onServer;
|
|
1373
|
+
for (const text of early) {
|
|
1374
|
+
const message = readHostMessage(text);
|
|
1375
|
+
if (message) session.receive(message);
|
|
1376
|
+
}
|
|
1377
|
+
return session;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* What a player is told who joins a game of the same base as theirs with other packs of content or mods
|
|
1381
|
+
* (`melee+gnomes` against `melee`): the host's and theirs. Nothing for games of other bases, or the same
|
|
1382
|
+
*/
|
|
1383
|
+
function otherPacks(host, own) {
|
|
1384
|
+
if (own === void 0 || own === host) return void 0;
|
|
1385
|
+
const [hostBase, ...hostPacks] = host.split("+");
|
|
1386
|
+
const [ownBase, ...ownPacks] = own.split("+");
|
|
1387
|
+
if (hostBase !== ownBase) return void 0;
|
|
1388
|
+
const list = (packs) => packs.length ? packs.join(", ") : "none";
|
|
1389
|
+
return `The host plays ${hostBase} with the packs ${list(hostPacks)}, this player with ${list(ownPacks)}: the host's are the game's`;
|
|
1390
|
+
}
|
|
1391
|
+
//#endregion
|
|
1392
|
+
export { ClientWorld, MAX_JITTER, RemoteSession, SessionHost, SessionRefused, connect, connectAs, fromRuns, loopback, toRuns };
|
|
1393
|
+
|
|
1394
|
+
//# sourceMappingURL=remote.js.map
|