Commit Graph

35 Commits

Author SHA1 Message Date
Acentech Dev
f8e856c7c6 feat(castle-siege): operate the Crown Switches by clicking, capture the crown by holding it
Some checks failed
.NET Core / build (push) Has been cancelled
The switches used to be held by simply standing near them, and the crown captured
by standing near it - clicking a switch only produced the client's "not implemented
yet" message. This drives both from the original interaction instead:

- Clicking a Crown Switch starts an operation which completes after
  CastleSiegeSettings.SwitchPushSeconds (15) and keeps the switch for the guild
  until its operator leaves the switch's area. One player per switch; anybody else
  clicking it is told another team is on it (C1 B2 14 state 2).
- While one guild holds both switches the crown's shield drops for it, and its
  guild master captures the throne by CLICKING the crown and holding it for
  CrownHoldTimeSeconds - seeded to 60 now, to match the countdown the client's
  registration panel hardcodes. The throne stays contestable until the siege ends.
- The shield now depends on the switches alone, as in the original; the gates and
  statues remain what they always were, the obstacle in the way.

The switch info packet (C1 B2 20) is broadcast before any switch-state packet
because the client's "switch released" handler reads its switch table without
checking that it exists - that table is only allocated when the info packet
arrives, so the wrong order crashes the client.

Also fixed while in here:
- The crown registration panel could never be closed: the cancel was only sent
  while the master still stood on the crown, which is precisely when the hold does
  NOT break. The panel is now closed for the player it was opened for.
- A contested switch was decided by enumeration order.
- Panels opened by the siege are closed when it ends.
- TryCaptureThrone was dead code carrying a second, diverged rule set.
- /csphase advertised the pre-refactor phase names to the client.
- The periodic broadcasts keyed off "UtcNow.Second % n", which silently skips when
  a tick runs late; they count ticks now.

The unit tests never compiled against the refactored model - they are migrated to
the state machine and guild ids, and cover the new switch and crown rules. A new
test proves update 105 writes the configuration into an existing database.

ApplyPendingUpdatesTool applies pending configuration updates without the admin
panel; it is [Explicit], so it never runs in a normal test pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 21:44:12 +03:00
Acentech Dev
6f7e58ff35 refactor(castle-siege): drive the cycle on the client's state numbers and persist guilds by id
Moves AdaMu's working Castle Siege onto the upstream data model that the
previous commit introduced, without changing how the siege plays.

State model
- CastleSiegePhase is replaced by DataModel's CastleSiegeState, whose values are
  exactly what the game client's CASTLESIEGE_STATE enum expects. The cycle now
  runs Idle1(0) -> RegisterGuild(1) -> Ready(6) -> Start(7) -> End(8) ->
  EndCycle(9) -> Idle1(0).
- Idle2(2), RegisterMark(3), Idle3(4) and Notify(5) keep their numbers for client
  compatibility but are never entered: AdaMu registers guilds directly and has no
  Mark of Lord step.

Guild identity
- Guilds are now identified by their persistent Guid instead of by name, so a
  rename (or a delete and re-create under the same name) can no longer hand
  castle ownership to the wrong guild. Names are carried alongside only for
  display and for the packets that send a name to the client.
- Interfaces.Guild deliberately has no id and the guild server's short ids are
  in-memory only, so the persistent id is resolved through the guild name once
  and cached per process. This avoids adding a method to IGuildServer, which
  upstream keeps changing.

Persistence
- The castle owner is stored in the CastleSiegeData row and the registrations in
  CastleSiegeGuildRegistration rows, replacing the previous plugin-configuration
  JSON blob. Only the current state and when it started still ride on the plugin
  configuration, because they have no column in the upstream schema.

Castle NPCs
- The hard-coded gate, catapult, crown and switch coordinates are gone. They are
  read from GameConfiguration.CastleSiegeConfiguration, seeded by
  CastleSiegeInitializer. Definitions flagged IsPersistedToDatabase are the
  breakable defenses and count towards the throne, which additionally brings in
  the 4 guardian statues the previous implementation did not spawn.
- The crown hold time now comes from the seeded configuration instead of the
  plugin settings.

The AdaMu operational settings (cycle durations, registration fee, designated
server id, auto-open schedule) moved to a renamed CastleSiegeSettings class, so
they no longer collide with upstream's CastleSiegeConfiguration entity.

Verified: full server build succeeds with 0 errors.
Not yet done: the 0xB2 0x00 CastleSiegeState request handler, and the docker /
local run.
2026-08-04 03:37:37 +03:00
Claude
452f0bb139 Describe chat commands in a machine readable way
Some checks failed
.NET Core / build (push) Has been cancelled
The only description of a chat command was its usage string, which is
meant to be read by a human. A user interface which wants to offer the
commands to a player needs the parts separately: the command, what it
does, and one entry per parameter.

Add ChatCommandInfo and ChatCommandParameterInfo, built from the
metadata which is already there - ChatCommandHelpAttribute for the
command and its required character status, ArgumentAttribute for the
short names and whether a parameter is required, ValidValuesAttribute
for the accepted values. Name and description come from the display
attribute, so they are returned in the language of the player.

The description which is passed to ChatCommandHelpAttribute was never
stored anywhere. Keep it as a fallback for commands which have no
display attribute, instead of discarding it.

The reset info command had its texts hard coded in English. It now
refers to resources like every other chat command does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSpK6jkyF8ZS5nYXyGwYxA
2026-07-31 15:09:32 +03:00
Acentech Dev
d03518deac fix(heykel-savasi): allow /starths restart + accept unencrypted team-select
Some checks failed
.NET Core / build (push) Has been cancelled
Two TvT (Heykel Savasi) event fixes found during mobile+PC client work:

- PeriodicTaskBasePlugIn: a forced start (GM /starths) no longer blocked while a
  previous run's TaskDuration window is still open. When an event finished early
  (team won / empty / before the 20-min duration) players are warped out but the
  periodic state stayed Started with a future NextRunUtc, so the guard returned on
  every tick and a re-start only worked after the full duration or a server
  restart. Force-finish the stale run when _isStartForced so the same execution
  falls through and prepares a fresh one; the NextRunUtc wait now applies to
  scheduled runs only.

- HeykelSavasiTeamSelectHandlerPlugIn: IsEncryptionExpected => false. The AdaMu
  client sends client-to-server packets unencrypted (like walk / animation /
  talk-to-npc), so the server was dropping the team-select (JOIN) packet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 22:59:38 +03:00
sven-n
0e2c94b84b Merge pull request #836 from nolt/speedhack-exempt-offline-players
Don't check offline players for speedhacking

(cherry picked from commit 6380b269770f0c949b03794ca701876c13636513)
2026-07-23 11:38:09 +03:00
sven-n
d04cbecae3 Merge pull request #820 from nolt/feature-bots
(cherry picked from commit b10de0645a869485fbb5771a739abd06b5708c2d)
2026-07-23 11:38:08 +03:00
sven-n
ed32ebdfd3 Merge pull request #827 from Rhefew/feature/ip-connection-limit
feat(plugins): implement customizable maximum concurrent connections

(cherry picked from commit 0e30df9ae22a265c3486dea1eba259567631eb87)
2026-07-23 11:35:43 +03:00
Acentech Dev
a2119b272e feat(tvt): 4 arena bosses at 3 statues, robust guard/boss spawn, reliable GM restart, English strings
- Bosses: 306/309/357/459 spawn once at (55,42)/(32,42)/(31,59)/(56,58) after 3 total statues broken.
- Guards/bosses spawn in a walkable BOX (retry) instead of a fixed point; several statue-guard
  offsets landed on non-walkable terrain and silently failed, leaving statues unbreakable.
- Guard-alive gate uses the ACTUAL spawned count so a statue can always reach 0 -> breakable.
- HeykelSavasiStartPlugIn.OnStartedAsync force-disposes a lingering finished context so GM /starths
  restarts the event immediately (no server restart).
- Statue-break + join messages translated to English.
2026-07-21 15:40:45 +03:00
Acentech Dev
bd5cda12dd fix(minigame): GM ForceStart bypasses the previous-run TaskDuration guard
A forced start (e.g. /starths) was blocked for the whole TaskDuration after a
run even though the event had already ended, so re-starting needed a server
restart. ForceStart now skips the 'previous event still running' guard;
scheduled auto-starts still respect it.
2026-07-21 15:02:45 +03:00
Acentech Dev
d41d46f24f chore(hs): rename event display name 'Heykel Savasi' -> 'TvT Event' (strings only)
Display strings/messages/designations changed everywhere (Name, entrance
messages, NPC designation, HUD title). Code identifiers/namespaces/commands
unchanged. DB rows updated separately.
2026-07-21 13:01:36 +03:00
Acentech Dev
7312b0e20d fix(minigame): announce entrance-opened for sub-minute registration windows
SendOpenedNotificationsAsync only looped per full minute, so a <1min entrance
(e.g. a 30s test window) sent no golden 'event opened' notification. Now sends
one up front when TotalMinutes==0. >=1min windows unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 12:55:00 +03:00
Acentech Dev
e0fafe7e03 feat(hs): /starths force-start command + /hsteam GM test-join command 2026-07-21 00:44:00 +03:00
Acentech Dev
b674e5a2cb feat(hs): periodic scheduled start plugin + config + state
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 00:38:57 +03:00
Acentech Dev
7e954b610f feat(hs): NPC talk plugin opens team panel when registration is open 2026-07-20 23:33:55 +03:00
Acentech Dev
fbe75d55be feat(CS): run the siege on ONE designated server (CastleSiegeServerId)
Some checks failed
.NET Core / build (push) Has been cancelled
With multiple game servers each has its own map instances, so the siege ran
independently on all of them - a guild could win uncontested on an empty server's
Valley of Loren. Now the siege (tick/spawn/battle/registration) runs only on the
server whose Id == config.CastleSiegeServerId (AdminPanel-editable). Other servers
skip the siege and just mirror the shared castle owner from config so the hunting-map
gate + castle flag rewards still work everywhere. The Guardsman on non-siege servers
tells players which server to switch to.
2026-07-15 19:10:48 +03:00
Acentech Dev
f135e90899 fix(CS): stop crown re-capture loop + show seal panel only to the master
Some checks failed
.NET Core / build (push) Has been cancelled
- The occupier can no longer re-register its own throne (canCapture = eligible != occupier),
  which caused a repeating 60s panel loop; only a DIFFERENT guild can contest.
- The 60s registration panel (0xB2/0x15) is now sent ONLY to the master on the crown,
  not broadcast to switch-holders/other players. Shield(0x16) + capture(0x18) still broadcast.
+1 test (15 total).
2026-07-15 16:22:13 +03:00
Acentech Dev
e5dfa24585 fix(CS): send crown shield packet (0xB2/0x16) only on change, not every tick
Some checks failed
.NET Core / build (push) Has been cancelled
The shield packet pops a modal message box on the client; sending it every tick
re-opened the modal continuously and froze player input at siege start. Track the
shield state in the context and only push the packet when it actually flips.
2026-07-15 15:36:51 +03:00
Acentech Dev
3dd4881406 feat(CS): authentic Crown-hold throne capture (S6 protocol) replacing Sinior-talk
Some checks failed
.NET Core / build (push) Has been cancelled
Break all gates + hold both switches (defenses down) -> the Crown shield drops
(C1 B2 16=0). The guild master then stands on the Crown (176,212) and holds for
CrownHoldDuration (default 60s, client shows a 60s countdown via C1 B2 15) to
capture; capture broadcasts C1 B2 18 + golden text and sets the occupier. Losing a
switch or leaving the crown resets the hold (contestable until the siege timer ends).
Sinior (223) is now informational guidance. +2 tests (14 total).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:04:10 +03:00
Acentech Dev
4651ef232c refactor(CS): schedule + periods are config-owned, AdminPanel-editable
Some checks failed
.NET Core / build (push) Has been cancelled
Move the auto-schedule from context-owned state to the plugin config (single source
of truth): RegistrationOpenDays + RegistrationOpenTimes + Registration/Preparation/
SiegeDuration are all editable in the AdminPanel plugin config and take effect live
(context refreshes its config reference each tick via UpdateConfiguration). /csschedule
now writes the config. Removed the duplicate Persisted schedule fields. 12 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:52:07 +03:00
Acentech Dev
f586321123 feat(CS): castle flag owner logo (C1 B9 02) + catapults for war atmosphere
Some checks failed
.NET Core / build (push) Has been cancelled
#1 Castle flags now show the owner guild's logo: resolve owner name -> guild logo
(cached), broadcast C1 B9 02 (32-byte mark) to players on the castle map every 15s.
#3 War atmosphere: spawn catapult NPCs 221/222 at siege start (client renders them
as siege weapons); the 0xB2/0x17 start flag already flips the map to warzone mode.
All raw S6 packets, no client changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:19:07 +03:00
Acentech Dev
7cf790c2a8 feat(CS): on-map siege countdown timer (S6 client protocol C1 B2 17/1E)
Some checks failed
.NET Core / build (push) Has been cancelled
Client already renders the siege-map countdown but never received the packets.
New ICastleSiegeStatusViewPlugIn + RemoteView impl send the raw S6 packets:
C1 B2 17 (battle start/stop flag, arms the countdown) and C1 B2 1E (remaining
hour/minute). CastleSiegeEventPlugIn broadcasts them to players on the battle map
every 10s during Siege and sends stop at Settlement. No client changes. +1 test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:45:01 +03:00
Acentech Dev
ac6700a356 feat(CS): weekly auto-schedule (day-of-week + UTC time), persisted, /csschedule GM cmd
Some checks failed
.NET Core / build (push) Has been cancelled
The siege can now auto-open registration on scheduled days/time instead of only
manual /csphase. Schedule is context-owned state (like owner), persisted across
restarts via the same config-JSON path. New GM command /csschedule sets/views/clears
it (e.g. /csschedule Sunday 20:00, UTC). +2 unit tests (11 total).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:47:29 +03:00
Acentech Dev
05a10d495d fix(CS): persist config via non-caching typed context (caching context saved nothing)
Some checks failed
.NET Core / build (push) Has been cancelled
CreateNewContext(config) returns a caching context whose returned config objects
aren't tracked by that context, so SetConfiguration+SaveChanges was a silent no-op
(DB row never updated). Load a fresh change-tracked PlugInConfiguration by id in a
non-caching typed context, rewrite the JSON, save. Adds a confirmation log line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:30:18 +03:00
Acentech Dev
9bdd57703f feat(CS): persist owner/phase/registrations across restarts via plugin config JSON
Some checks failed
.NET Core / build (push) Has been cancelled
Castle Siege state was in-memory, so the castle owner (and thus the P4 hunting-map
reward) reset on every server restart/redeploy. Now the context marks itself dirty
on any persistable change (phase transition, registration, owner set); the plugin's
periodic tick writes a snapshot into its own PlugInConfiguration CustomConfiguration
(a JSON blob already stored in PostgreSQL - no schema migration) and restores it on
startup. Battle state (defenses/switches/occupier) stays transient. +2 unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:17:34 +03:00
Acentech Dev
b890e82a88 refactor(CS-P3): siege defenses = 6 castle gates only (removed 4 statues per user - kept clean gate mechanic)
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-15 11:18:28 +03:00
Acentech Dev
8114930855 feat(CS-P3): 4 real guardian statues (283) at castle positions 82/107/94,129/131/126 - all defenses required for throne 2026-07-15 10:53:57 +03:00
Acentech Dev
af9554bf91 feat(CS-P3): REAL castle gates (NPC 277) at 6 client gate positions -> client closes+blocks them; breakable via HP template; inner guardian statues (283)
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-15 03:18:00 +03:00
Acentech Dev
72ebc96e28 feat(CS-P3): throne registration via Sinior/Crown talk (223/216) after defenses down + both switches held; reachable defense positions
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-15 02:49:27 +03:00
Acentech Dev
db8bdd394f feat(CS-P3): PROPER siege - position-based Crown Switch hold (stand on them) + gates & statues destructibles + auto throne capture; remove non-working talk handlers
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-15 02:28:14 +03:00
Acentech Dev
c50af5e5c2 feat(CS-P3): full siege objective chain - guardian statues (Destructible spawn) + dual Crown Switch hold + throne capture gating 2026-07-15 01:52:19 +03:00
Acentech Dev
5549c2bec7 feat(CS-P3): siege PvP on Valley of Loren during siege (ADAMU-CUSTOM) + golden phase announcements
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-15 01:23:57 +03:00
Acentech Dev
c7147f7a63 feat(CS-P3): warp registered members to Valley of Loren on siege start + Crown Switch throne capture (NPC 217/218)
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-15 00:57:28 +03:00
Acentech Dev
268d40acd1 feat(CS-P1): GM chat commands (/csstatus /csstart /csphase /cssetowner /csreset)
Some checks failed
.NET Core / build (push) Has been cancelled
2026-07-14 22:44:46 +03:00
Acentech Dev
6999a780f3 feat(CS-P1): periodic Castle Siege plugin (per-context tick + custom config) 2026-07-14 22:41:46 +03:00
Acentech Dev
36fc125d5c baseline: OpenMU upstream b5a0961 (fresh source) 2026-07-14 19:00:35 +03:00