Co-op: friends share progress across their own games (proposal and claim, before any pull request) #2269
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot#2269
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The idea
Co-op: two to eight friends each play their own game with their own save, and progress is shared. A chest, a boss, a song or an item one of them gets is in everyone's game too; whoever makes the room sets which parts are shared (progress, items, songs, quest items, keys, rupees, ammunition). It is co-operative play of the single-player game. It is not a versus mode (#1522) and not emulator netplay: nobody's game drives anyone else's controls or camera. Showing the other players' Links in your world could come later, off by default.
I would like to claim this, and I am asking before opening any pull request because it adds something Hookshot does not have today: a network socket. The code exists and is tested as far as described below; the questions at the end are the ones I cannot answer for you.
What it adds to the host
scripts/coop_relay.py, Python standard library, 869 lines). Nothing listens on the player's machine and there is no default server. The game's ownnn::socketstays offline as it is.third_party/monocypher/).What it does to saves
Nothing from a room reaches a player's game until that player accepts the room on a Co-op tab, which lists the players and their key fingerprints. At that moment the whole save folder is copied to
coop-backup/beside it, and nothing is applied before the copy exists. Values are applied through the game's own setter routines, called from the host, and only raised (a flag set, a count up to its maximum), never lowered; a value outside the game's own table is refused.The pull requests I have in mind, each building and passing
tests/run_all.pyalone:feat(coop): a relay and an encrypted wire format for co-op roomsfeat(menu): text fields in the Insert menufeat(coop): join a room from the runtimefeat(menu): the Co-op tabfeat(coop): progress syncfeat(coop): show the other playersWhere the code stands (on my fork, not proposed for merge yet:
coophas 1, 3 and the merge engine of 5;menu-text-fieldshas 2):What I am asking you to decide
MkwVkHostmember and anMKWVK_API_VERSIONbump. Would you take text fields as a pull request of their own first?host/nn_fs.c. The copy takes a lock that the save mount's writes also take, because there is no transaction to hook onto. Acceptable in that file?If the answer to 1 is no, I will keep it out of your way as a fork and say so here.
Anything else
docs/workflow.md: S3, a feature.Two updates:
I'm watching this thread and will answer here.
Progress: two games in one room now share progress. The code is on my fork, branch
coop-sync: the three commits listed above and a fourth,feat(coop): progress sync(the game side and the save copy).The session: two builds of that branch on one PC under Linux, game A on an RTX 4090 (NVIDIA 615.78.08), game B on an Intel UHD 770 (Mesa 26.2.5), both without a window, each with its own user folder and its own copy of the same save (Kokiri Forest, no sword), and the relay on loopback.
coop: room … created (member 1); B:coop: joined room … as member 2; the relay saw boththe save as it was before this room is in …/coop-backup/first-…; the copy equals B's starting save file by file (14 files)QueryGetPouchContentNumByActorName)SetBool; A receives it, applies it, and A's getter reads it setThe session script's checks: 28 pass, 0 fail, 4 not applicable (puppets and the menu are later parts). No fatal line, no device lost, and no driver fault line in the kernel log for either GPU.
One thing it settled for the design: an item arrives through the flags the game itself keeps (
SetBool/SetInt), not through the game's item routine. So it comes without the "you got an item" message and pose, and what Link wears stays each player's choice.Not shown yet:
On the Intel GPU game B's picture has false colours for a reason that is not co-op's (known on that device, being fixed separately); these checks read the logs and the save, not that picture.
Added 13:46 UTC: items both ways while both games play. A further session on the same machine and build: the Deku Shield and the Boomerang given on B, the Fairy Slingshot and the Fairy Ocarina given on A, one every 17 seconds. At the end each game's own inventory count read all four, and the Kokiri Sword, as 1 in both games. B applied 16 values and A applied 5, and all 21 read back from the game afterwards (16 on/off flags through the game's getter, 5 counts from its table). No datagram was thrown away. 29 checks pass, 0 fail, 4 not applicable; no fatal line, no device lost, no driver fault line in the kernel log for either GPU.
Thanks for the report. Reports of the same problem are now collected in #1007, which lists this one with your details and links back here, so one fix closes them together and the open list stays readable. Follow #1007 for the fix, and add new details there (or here). If this turns out to be a separate problem it will be reopened. Pull request #2274 works on this.
Progress, here for those who follow this thread (the tracker for it is #1007, the code is in #2274):
Draft #2274 assembles the relay and encrypted format, merge engine, runtime session, progress sync, text fields and Co-op tab as six commits: head
71d4ab0f, on mainfce53453. The description names the head used by each two-game session; an earlier head also delivered the Kokiri Sword from a real chest to the second game's inventory.To try it, build the branch, run
python -I scripts/coop_relay.py --port 24680somewhere both games can reach by UDP, and use the Insert menu's Co-op tab to enter a name and relay address, make/join a room, then load a save and press Accept the room twice in each game. Read the key fingerprints to each other first; each save is copied before room values are applied. Invite only people you trust with your save.This remains a draft, not an all-green or complete shared-world claim. The final-head suite was 458 passed, 2 failed, 20 skipped: main's isolated
HOS_FPS_LOGenvironment-rule failure, and the co-op session fixture's thread-count failure under load. The complete standalone session test subsequently passed all 8 runs with GCC and all 8 with Clang on unchanged session code. Other players' Links, warp, combat and shared enemies are separate work, not in that draft; two PCs and Windows remain untested. The socket-policy question and the restarted maker's missing-rules case still need maintainer decisions.