ARRAY-OnlineCoop icon

OnlineCoop

Online co-op for up to 4 players: see each other in the run, follow the host's route, share enemy damage with party-size HP scaling, and roll your own boons and relics.

By ARRAY
Last updated 9 hours ago
Total downloads 8
Total rating 0 
Categories Mods Gameplay AI Generated
Dependency string ARRAY-OnlineCoop-0.1.10
Dependants 0 other packages depend on this package

This mod requires the following mods to function

Hell2Modding-Hell2Modding-1.0.112 icon
Hell2Modding-Hell2Modding

Lua Mod Loader for Hades 2

Preferred version: 1.0.112
LuaENVY-ENVY-1.2.0 icon
LuaENVY-ENVY

A plugin to allow ReturnOfModding plugins greater control of their environment.

Preferred version: 1.2.0
SGG_Modding-Chalk-2.1.2 icon
SGG_Modding-Chalk

System for handling config files in ReturnOfModding

Preferred version: 2.1.2
SGG_Modding-ReLoad-1.0.3 icon
SGG_Modding-ReLoad

Provides helpers for managing hot reloading and waiting for the right time to load

Preferred version: 1.0.3
SGG_Modding-ModUtil-4.0.1 icon
SGG_Modding-ModUtil

Utility mod for mod interactions within lua for SGG's games

Preferred version: 4.0.1

README

Hades II Online Co-op (up to 4 players)

Play Hades II online with up to 3 friends. Everyone who has the mod sees the other players running around the same rooms, the party follows the host's route through the Underworld or the Surface, enemies are fought together, and every player gets their own boons, relics and shop rolls. The rest of the game is untouched.

Status: v0.1.10. Loads in Hades II on Hell2Modding 1.0.112. Players connect, see each other and play runs together. In the first runs on 0.1.5 most kills reached everyone, but a few enemies stayed alive on one screen after a room was won, and a friend whose game window went to the background dropped out and ended up on their own route. 0.1.6 gives every world the same enemies, ends a room for everyone when one player wins it, and reconnects dropped players. 0.1.7 closes the Hell2Modding menu by itself (it turns V-Sync off, which crashed one player's graphics driver) and logs every join attempt. On 0.1.7 a party of three played through Erebus and Oceanus together, but the friends' games stopped with "corrupted save data" in the Fields of Mourning. 0.1.8 fixes that. Up to 0.1.8, friends' doors showed other rewards than the host's (extra health on one screen, extra Magick on another); 0.1.9 gives every door the host's room, reward and god. 0.1.10 brings friends back out of Chronos' Asphodel rift with the host, and adds a door there that skips it. The networking, sync and lobby window logic are covered by offline tests (see tests/), but the door matching hasn't been played yet. Read How it works below so you know what to expect.

How it works

Every player runs their own copy of the game ("mirrored worlds"). The mod keeps those copies in step:

What the mod does
Seeing each other Other players appear as tinted Melinoe figures with a name and HP tag (P1 gold, P2 blue, P3 green, P4 pink), in the Crossroads and during runs, whenever you are in the same room.
Same route The host heads out of the Crossroads first; friends' exit stays shut until then. Friends then get the host's first room. Every door shows what the host's shows: the same room, reward and god, the same shops and Chaos gate, the same Fields cages. When the host walks through a door, everyone else's next door leads to the same room, with the same map seed, mirrored the same way.
Same enemies Each room's fight is the one the host's game rolled: the same waves, the same enemy kinds and counts (the host's completed runs and boons set the counts), spawning in the same order in every world.
Rooms start together A room's enemies wait to spawn until everyone in the party has walked in (up to 45 s in a run's first room, 20 s after that).
Fighting together Every enemy exists in every player's world. Your hits (armor included) and your kills are copied to the same enemy in everyone's world, so the same enemies die for everyone.
Rooms end together When one player wins a room, it's won for everyone: a world still fighting stops spawning and its last enemies go 2 seconds later. A friend who walks in after the room was won skips the fight. Boss and miniboss fights always play out in every world.
Multiplayer scaling Enemy HP is multiplied by party size: +75% per extra player by default (2p 1.75x, 3p 2.5x, 4p 3.25x). Adjustable from 0 to +200% per player.
Your own loot The god behind a door is the host's, unless your run can't offer that god yet or your keepsake picks your next one. The boons each god offers you, Daedalus Hammers, shop stock and what's inside other rewards are rolled separately for each player. Keepsakes, Arcana and weapons are your own as always.
Doors The host's doors stay shut until everyone in the room (or one room behind) has cleared it; the host can use a blocked door 3 times in a row to go anyway. Friends' doors open when the host's do (up to 60 s; a door whose host room comes later changes to match), stay shut until the host picks one, and can't be forced, so nobody drops off the route. Only the host can reroll a door. If the host dies or leaves, friends' doors open onto their own route.
Dropped connections A friend who loses the host (game in the background, VPN hiccup) reconnects on their own for up to 2 minutes, in the same slot, and stays on the host's route. Their doors wait for the host meanwhile, and the host's doors wait up to 60 seconds for them to come back.
Deaths When you die your run ends as usual and you go back to the Crossroads; your friends see that you fell and carry on.

Limits you should know about

These come from what Hades II mods can do today (Lua scripts on top of Hell2Modding; the engine itself has no netcode):

  • Other players are visual ghosts, not physical heroes in your world. Their attacks don't hit your copy of the enemies directly; instead the damage they deal in their world is copied onto your enemies. Enemies in your world chase and attack only you, so each player still dodges their own copy of the room's attacks.
  • Enemies won't stand in exactly the same spot on every screen. Their health and deaths are shared, their movement is not.
  • Bosses are never force-killed by a friend. Your friends' damage stops just above a boss's next phase threshold (or at 1 HP), and your next hit finishes the phase, so boss phase scripts still run normally.
  • Ghost animation is basic: idle, run and dash, facing the direction the player faces. Attack and cast animations aren't mirrored yet.
  • Keep Hades II as the active window. The game stops running while it's in the background, so the others stop hearing from you. After 30 seconds they count you as dropped; you reconnect on your own when you come back.
  • Everyone should use the same Oath of the Unseen vows (Fear) as the host. Some vows change which enemies spawn, and then enemies are matched by kind and place instead (below).
  • Enemies are matched by spawn, with a fallback. Every world spawns the host's waves in the same order, so a hit or kill lands on the same spawn everywhere. If a room's spawns don't line up (different vows, a spawn that failed in one world), that room falls back to the nearest unpaired enemy of the same kind, and a kill whose kind never shows up takes the nearest other wave enemy after 3 seconds. Minions (eggs, summons) spawn separately in each world, so a friend's minion kill only takes a minion that's already there. The Log section in the window sums up what was shared in each room.
  • In the Fields of Mourning, everyone opens their own reward cages. Every world gets as many cages as the host's room, holding the same rewards and gods (the boons offered are still your own), but each cage's fight is rolled in each world. Open and clear the cages in your own game. Their enemies are matched by kind and place, and a cage fight won elsewhere may still need finishing in yours. Other rooms with several fights work the same way.
  • The ship wheels in the Rift of Thessaly still show each player's own rewards. Doors everywhere else show the host's.
  • Joining mid-run works, but you catch up room by room. A friend who joins after the host left the Crossroads gets the host's route when they head out and walks through the same rooms from the start.
  • The host needs a reachable UDP port (see Connecting over the internet).

Install

With r2modman (recommended)

  1. Install r2modman and pick Hades II.
  2. In your profile, install these from Online: Hell2Modding, ENVY, ModUtil, Chalk, ReLoad (any recent version works; installing ModUtil usually pulls in the rest).
  3. Settings > Import local mod, choose Array-OnlineCoop-0.1.10.zip.
  4. Click Start modded. Every player needs the same mod version.

Manual

  1. Install Hell2Modding (d3d12.dll goes next to Hades2.exe in Hades II/Ship/) and its Lua libraries ENVY, ModUtil, Chalk and ReLoad, each in its own folder under Hades II/Ship/ReturnOfModding/plugins/.
  2. Create Hades II/Ship/ReturnOfModding/plugins/Array-OnlineCoop/.
  3. From the zip, copy manifest.json and everything inside plugins/ into that folder.

Mac

Hell2Modding (and so every Hades II mod) only runs on the Windows version of the game. On a Mac that means CrossOver with the Windows Steam client, following the Hades II Mod Wiki's Mac guide. The overlay window can crash there, so in the mod's config set EnableOverlayUI = false and use AutoConnect = 'host' or 'join' (with JoinAddress) to connect at startup instead.

Playing

  1. Launch the game. Press Hell2Modding's menu key (INSERT unless you changed it) to open the overlay; the Online Co-op window lives there. If it's hidden, tick it in the overlay's Windows menu. The menu closes by itself when you click Host game or Join game, and after 40 seconds, because Hell2Modding turns V-Sync off while it's open (see Troubleshooting).
  2. Host: type your name, keep port 27777 (or pick another), click Host game. Only the host clicks Host game. Until someone joins, your party HUD shows the address friends type ("Friends join 26.x.x.x (Radmin)").
  3. Friends: type your name, the host's address (not 127.0.0.1, which is your own PC) and the same port, click Join game.
  4. Meet in the Crossroads (you'll see each other). The host starts the run first, then everyone else heads out. Friends' exit stays shut until the host has gone.
  5. A small party HUD (name, HP, room, ping) stays on screen; turn it off in the window. If hosting or joining stops, the HUD says why for a minute.

Settings in the window are saved to the mod's config file. Party rules that shape the route (waiting at doors, HP scaling) should match between players; the host's choices are the ones that matter for doors.

Connecting over the internet

The host's UDP port (default 27777) must be reachable from your friends:

  • Easiest: everyone installs Radmin VPN (free, no account). The host creates a network, friends join it with its name and password, then join the host's Radmin IP (26.x.x.x). Tailscale or ZeroTier work the same way. No router changes.
  • Or: forward UDP 27777 on the host's router to the host PC, allow Hades2.exe through Windows Firewall, and friends join the host's public IP.
  • Same house / LAN: use the host's local IP (e.g. 192.168.1.20).

Troubleshooting

  • The game crashes about a minute after you open the menu (the crash log says the graphics device hung or was removed): Hell2Modding turns V-Sync off while its menu is open, and some graphics drivers hang after about a minute of that. Update your graphics driver. The menu closes itself after 40 seconds (MenuAutoCloseSeconds in the mod's config; 0 closes it only after Host or Join). A host can set AutoConnect = 'host' to start hosting at launch without opening the menu at all.
  • "Could not reach host": wrong address/port, port not forwarded, or the firewall blocked it. Try Tailscale. The host's HUD shows the addresses to use.
  • "Different mod versions" (or "Mod version mismatch" from 0.1.4 and older): everyone needs the same version of this mod.
  • "Corrupted save data" for a friend in the Fields of Mourning (their log shows RoomLogic.lua:5689: bad argument #1 to 'ipairs'): a bug in 0.1.7 and older, where a friend's Fields room had no reward cages. Everyone updates to 0.1.8 or later. The stopped run itself is lost, but the game falls back to its last Crossroads save, so earlier progress is kept.
  • "Wait for Host to start the run first": friends' Crossroads exit opens once the host has headed out.
  • "Waiting for ... to arrive": the room's enemies wait until everyone has walked in.
  • "Your doors open when Host's do": your room is cleared, but the host's doors haven't opened yet (the host is still on the way in, or still busy in the room). Yours open with the host's rooms behind them as soon as the host's do, or after 60 seconds; a door whose host room comes later changes to match.
  • "Only Host can reroll doors": everyone goes where the host goes, so a friend's reroll would change nothing. It isn't spent.
  • "Not synced: host should start the run first": the client left the Crossroads before it could see the host. Die or abandon the run, and let the host lead.
  • "Lost connection to Host. Reconnecting..." / "Reconnecting to Host..." on a door: you stopped hearing from the host (usually a game window in the background, on either side). It reconnects by itself within 2 minutes once both games are running; your door opens when the host's next room is known. Your kills and room clears from meanwhile go out once you're back.
  • "Waiting for ... to reconnect" (host): a friend dropped. The door opens when they're back, after 60 seconds, or when you use it 3 times.
  • The overlay window's Log section lists which game hooks were found and any errors. Please include it in bug reports, together with Hades II/Ship/ReturnOfModding/LogOutput.log.
  • A join fails and nobody knows why: both players' LogOutput.log have [OnlineCoop] net: lines for every attempt: the version each game loaded, where the host listens and its addresses, who connected, who was turned away and why (another mod version, a full lobby), "no answer from" when the host never heard you, and failing sends.

For developers

  • src/net/ contains the transport: UDP over LuaSocket (require('socket'); Hell2Modding 1.0.112 has no rom.socket), host-relayed star topology, a reliable ordered event channel with acks and resends, an unreliable 20 Hz state channel, and automatic rejoin into the same slot after a timeout.
  • src/game/sync.lua hooks the game via ModUtil (StartRoom, SetupUnit, DamageEnemy/Damage, Kill, HandleEnemySpawns, HandleNextSpawn, GetNextSpawn, CalcTotalSpawns, AttemptUseDoor, UseEscapeDoor/UseDreamRunDoor, LeaveRoom, ChooseEncounter, SpawnRewardCages, StartNewRun, ChooseStartingRoom, RandomSetNextInitSeed, RandomSynchronize, the encounter generators, UnlockRoomExits/DoUnlockRoomExits, CreateDoorRewardPreview, AttemptReroll, KillHero, OnAnyLoad). Hooks that don't exist in your game version are skipped and listed in the log.
  • The host sends each room's encounter as it's chosen (inside LeaveRoom). Friends build the same encounter from it, and every world runs its spawn choices (GetNextSpawn, HandleNextSpawn, CalcTotalSpawns) on seeds derived from the encounter and the spawn number, then puts the game's own random stream back, so loot stays independent. After DoUnlockRoomExits the host sends a door message for each door (room, reward and store, the Boon's god or a Trial's two gods la/lb, room rolls fl, cages and their gods cg/ct/cl, the room's other version uo, and the door's id, name and place) and then a doors marker. A friend's DoUnlockRoomExits waits for them when UnlockRoomExits calls it, and each door gets the host's room just before CreateDoorRewardPreview draws it; doors whose host room comes later are drawn again. Encounter and seed messages say whether the host's map is mirrored (fp).
  • Enemies get a shared id (<slot>.<n>) the first time a player hits them, and messages carry the enemy's spawn key (<encounter>#<wave>.<n>). Other worlds pair a new id with their enemy of the same spawn (or, when spawns don't line up, the nearest unpaired one of that kind, wave enemy for wave enemy), buffer messages for rooms they haven't reached, hold kills for spawns they haven't reached, and drop kills for spawns already dead there.
  • src/ui.lua holds the ImGui lobby and HUD.
  • Tests: lua5.2 tests/net_test.lua (4 peers, a 5th rejected, a peer on another version rejected, 30% packet loss, dropped players rejoining), lua5.2 tests/sync_test.lua (two worlds against a fake game API, including its wave loop, encounter generation and door setup) and lua5.2 tests/ui_test.lua (lobby and HUD against a fake ImGui). The first two need LuaSocket.

MIT licensed. Contains AI-generated code.