Repository navigation
Conversation
Mosch0512
added a commit
to Mosch0512/MuMain
that referenced
this pull request
Oct 29, 2026
Devil Square's stages and Play in Monster Attacks, asked while the attacks PR was open: - Q-MN70: OpenMU sends the extended client the discriminator of its map in MapChanged byte 10 (Mosch0512/OpenMU#7), the stage of Devil Square; other clients get 0 there as before. Q-MN66's "not needed" changes. - Q-MN71: map lists name a stage as {"map": 9, "stage": 2}; a number names all its stages; the picker shows the seven stages one by one. - Q-MN72: a group's levels are maps of their own or stages of one map. - Q-MN73: Play on each skill row and Play next attack, in Monster Preview with the attack sounds; skill effects come with the skill tools.
Mosch0512
added a commit
to Mosch0512/MuMain
that referenced
this pull request
Oct 29, 2026
Devil Square 1-4 are stages of map 9 and 5-7 of map 32, so data could pick "Devil Square 1-4" but not "Devil Square 2" (Q-MN70, Q-MN71). - MapChanged (C3 1C 0F) carries the stage at byte 10 from OpenMU to the extended client (Mosch0512/OpenMU#7); GetTeleportStage reads it when the packet is long enough. The map manager keeps it next to the server map (GetServerStage, 0 where WorldActive was set alone); entering the world and a revival set 0. - Every map list in data (map variants, mapTextureFolders, a part's maps, soundEventMaps) may name a stage, {"map": 9, "stage": 2}, next to plain numbers; a number names all its stages. Data::Maps::MapRef reads, writes and matches them in one place. A map and its stage in two variants of a monster is fatal like a map twice. - The map picker lists Devil Square's seven stages in its row; any of them can be picked, a map whose every stage is picked is written as its number, and the row's info names the stages.
GameMapDefinition.Discriminator tells maps with the same number apart: the stages of Devil Square are seven definitions on maps 9 (1 to 4) and 32 (5 to 7). The extended client needs it to know the stage it is in. MapChangedExtended (C3 1C 0F) is MapChanged with the discriminator appended at index 15, after the 15 bytes of the original, which stays as it was. A client can tell by the length whether a server sends it: an older server sends the 15-byte MapChanged, whose bytes 10 to 14 are not initialized. The structs, the connection extension and the packet tests come from the XSLT transformations of the project, the markdown documentation from GenerateMarkdownDocs.xslt run with Saxon (the build runs it with npx xslt3).
…he extended client MuMain (the extended client, 106.3) needs to know which stage of Devil Square a player is in: its monster data can make a monster attack differently on one stage, and the map number alone does not tell the stages of a map apart. MapChangeExtendedPlugIn and TeleportExtendedPlugIn send MapChangedExtended with the discriminator of the player's map. Other clients keep MapChangePlugIn and TeleportPlugIn, and the original MapChanged. A discriminator which doesn't fit into a byte is sent as 0, instead of being cut to the one of another map. Tests: the extended client gets the discriminator with a map change, a teleport and a failed map change, a Season 6 client the original packet. PlayerTestHelper can now log in any player, so the remote player of the tests enters the world the usual way instead of getting its character set by reflection.
…inator A character which loses its connection during Devil Square can log in again and rejoin the game on its stage. That needs no map change: the mini game puts the character on the map of its stage before the character information is sent, so MapChangedExtended never tells the client the stage then. CharacterInformationExtended (C3 F3 03) gets the discriminator at index 92, which makes it 93 bytes long, and UpdateCharacterStatsExtendedPlugIn sends the one of the character's map. As with MapChangedExtended, a client can tell by the length whether a server sends it. The structs, the connection extension and the packet tests come from the XSLT transformations of the project, the markdown documentation from GenerateMarkdownDocs.xslt run with Saxon. Test: the extended client gets the discriminator when it enters the world on a stage of Devil Square.
Mosch0512
force-pushed
the
feat/map-discriminator
branch
from
October 29, 2026 12:36
36c2e9c to
4d58c21
Compare
Mosch0512
added a commit
to Mosch0512/MuMain
that referenced
this pull request
Oct 29, 2026
…ing the world Mosch0512/OpenMU#7 no longer writes the discriminator into byte 10 of the original MapChanged, which stays as it was (OpenMU's coding rule 2). The extended client gets it at the end of two packets that are one byte longer than the originals: - MapChangedExtended (C3 1C 0F, 16 bytes) at 15, with a map change or a teleport. GetTeleportStage reads it there; the 15-byte MapChanged has none, and its bytes 10 to 14 aren't initialized. - CharacterInformationExtended (C3 F3 03, 93 bytes) at 92, when the character enters the world. A character which lost its connection in Devil Square logs in again on its stage's map without a map change, so entering the world sets the stage from the packet (GetJoinMapServerStage) instead of 0, before LoadWorld loads the stage's map parts. The struct stays 92 bytes, so safe_cast still takes the packet of a server which doesn't send the stage. Q-MN70 in the design and docs/monster-data.md name the two packets.
The for loop moved its counter in the body (Sonar S127, reported by Codacy on MUnique#1346). Same packets, same failure without a header.
The generated senders write only the fields of a packet, so its unused bytes kept what an earlier packet left in the reused output buffer: bytes 10 to 14 of MapChangedExtended and byte 89 of CharacterInformationExtended (review of MUnique#1346). The map change, the teleport and the character information of the extended client write into a span which is cleared first, as OpenNpcWindowPlugIn does. The remote view tests write into memory which is filled with 0xFF, and a test checks the unused bytes.
CancelPendingFlush and Complete had empty bodies (Sonar S1186) and FlushAsync ignored its cancellation token (Sonar S1172), reported by Codacy on MUnique#1346. The empty methods now say why nothing happens, and a flush with a cancelled token throws, as it does on a real pipe.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MuMain needs to know which stage of Devil Square a player is in: its monster data can make a monster attack differently on one stage (map variants, MuMain's monsters design D67), and the map number alone does not tell the stages apart. OpenMU already does: the seven Devil Square stages are map definitions on number 9 (stages 1–4) and 32 (stages 5–7) with
Discriminator1 to 7. This sends that discriminator to the extended client.The original
MapChangedpacket stays as it was (coding rules, rule 2): the extended client gets new or longer…Extendedpackets.Commits
MapChangedExtended(C3 1C 0F, 16 bytes) isMapChangedwithMapDiscriminatorappended at index 15, after the 15 bytes of the original. A client can tell by the length whether a server sends it: an older server sends the 15-byteMapChanged, whose bytes 10 to 14 are not initialized.MapChangeExtendedPlugInandTeleportExtendedPlugIn([MinimumClient(106, 3)]) sendMapChangedExtendedwith the discriminator of the player's map; other clients keepMapChangePlugIn/TeleportPlugInand the original packet. A discriminator which doesn't fit into a byte is sent as 0, not cut to the one of another map. Plugin names and descriptions in en, de and zh-CN.CharacterInformationExtended(C3 F3 03) getsMapDiscriminatorat index 92 and is 93 bytes long. A character which loses its connection in Devil Square can log in again and rejoin its stage without a map change; the mini game puts it on the stage's map before the character information is sent, so this is how the client learns the stage then.forcounter in its body (Sonar S127, reported by Codacy on feat: send the map's discriminator (Devil Square stage) to the extended client MUnique/OpenMU#1346).The structs, connection extensions and packet tests come from the project's XSLT transformations. The markdown documentation comes from
GenerateMarkdownDocs.xslt, run with Saxon (the build runs it withnpx xslt3); on master's XML it reproduces all 336 committed packet documents byte for byte.Testing
MapDiscriminatorRemoteViewTests: a remote player logs in throughPlayerTestHelperand warps to a Devil Square stage. The extended client getsMapChangedExtendedwith the stage for a map change, a teleport and a failed map change (also for discriminators 257 and -1, which it gets as 0), andCharacterInformationExtendedwith the stage when it enters the world on a stage. A Season 6 client gets the 15-byteMapChanged.MUnique.OpenMU.Network.Packets.Tests: 650/650.MUnique.OpenMU.Tests: 2507 passed, 2 skipped, 3 failed. The 3 are Discord text tests (GuildShowsMasterAndOnlineMembersAsync,WhoShowsCharacterAsync,SetupReportListsCreatedAdoptedAndFailed), which expect\nand get\r\non Windows; they don't touch this change.MapDiscriminatorRemoteViewTestspasses again (5/5).The MuMain side (Mosch0512/MuMain#135) has to follow: read the discriminator at index 15 of a 16-byte C3 1C 0F, and at index 92 of a 93-byte C3 F3 03.