-
Notifications
You must be signed in to change notification settings - Fork 408
Fork contributions: localization, admin UI, summon refactor, Docker/logging, and docs #649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
AdminPanel ES + presets Docker LAN/Public (RESOLVE_IP) y ajustes S6
s6-lan-presets-es
…nish-and-english docs: add bilingual readme and fork changelog
…nish-and-english-q7sxr5 fix: apply custom drop to invasion boss
…anol feat: add Spanish translations for server and panel messages
…se-payload Send Chaos mix success rate and bonus to client
…sa-de-la-elfa Add plugin for customizable summoned monsters and fix summond skill
…configurationplugin refactor: remove plugin inheritance from summon config
…ping upstream FixItemRequirements2=61 and shifting fork-specific updates to 62-66; retain ElfSummonDefaults=250
Sync upstream
Sync upstream
…N/ES) and update fork changelog
…tem-for-translations Localize remaining server interactions
fixing AI's nonsense
fixing AI's nonsense
fixing AI's nonsense part3
Summary of ChangesHello @EmanuelCatania, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request integrates significant improvements from a fork, focusing on enhancing the OpenMU server's usability, configurability, and stability. Key areas include comprehensive localization, a revamped Admin Panel with live log tailing, a refactored and more flexible summon system, and new event data for Rena, Golden Archer, and White Wizard. Additionally, it brings robust Docker configurations and refined logging mechanisms, making the server easier to deploy, manage, and debug. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This is a massive and impressive pull request that introduces significant improvements across the entire project. The addition of a localization system is a major step forward for internationalization, and the refactoring of the summon logic appears to be much more robust and feature-rich. The enhancements to logging, Docker setup, and the Admin Panel (especially log tailing) are also very valuable contributions. The code is generally of high quality. My review focuses on ensuring the new localization feature is used consistently and points out a couple of minor issues in documentation and code.
| if (arguments.Count < requiredArgumentCount) | ||
| { | ||
| throw new ArgumentException($"The command needs {requiredArgumentCount} arguments and was given {arguments.Count}.", nameof(command)); | ||
| throw new ArgumentException($"El comando requiere {requiredArgumentCount} argumentos y se recibieron {arguments.Count}.", nameof(command)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception message here is hardcoded in Spanish. Exception messages in core utility functions like this should generally be in English for consistency in logging and debugging across different development environments. Hardcoding a specific language can make it difficult for developers who don't speak that language to understand errors.
throw new ArgumentException($"The command needs {requiredArgumentCount} arguments and was given {arguments.Count}.", nameof(command));| foreach (var requiredProperty in requiredProperties) | ||
| { | ||
| stringBuilder.AppendLine($"The required argument named {requiredProperty.Name} was not used."); | ||
| stringBuilder.AppendLine($"El argumento obligatorio '{requiredProperty.Name}' no fue especificado."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| catch | ||
| { | ||
| throw new ArgumentException($"The argument {propertyInfo.Name} was given a invalid type, it expects the value to be of the type {propertyInfo.PropertyType.Name}."); | ||
| throw new ArgumentException($"El argumento {propertyInfo.Name} tiene un tipo inválido, se esperaba {propertyInfo.PropertyType.Name}."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exception message is hardcoded in Spanish. It's recommended to use English for exception messages in shared utility code to ensure they are understandable by all developers.
throw new ArgumentException($"The argument {propertyInfo.Name} was given an invalid type; it expects the value to be of type {propertyInfo.PropertyType.Name}.");| @@ -0,0 +1,314 @@ | |||
| # OpenMU Project / Proyecto OpenMU | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ### Deploy from a remote fork (compose overlay) | ||
|
|
||
| - Overlays in `deploy/all-in-one` allow building the image directly from your fork using a remote git context. | ||
| - Required env var: `OPENMU_FORK_CONTEXT` in the form `https://github.com/<user>/OpenMU-<fork>.git#<branch>:src`. | ||
| - Example commands (server): | ||
| - `export OPENMU_FORK_CONTEXT="https://github.com/EmanuelCatania/OpenMU-S2.git#master:src"` | ||
| - `docker compose -f docker-compose.no-nginx.yml -f docker-compose.override.yml -f docker-compose.public-dns.yml -f docker-compose.npm-net.yml -f docker-compose.from-fork.yml build --no-cache --progress=plain --build-arg APP_UID=1000 openmu-startup` | ||
| - `docker compose -f docker-compose.no-nginx.yml -f docker-compose.public-dns.yml -f docker-compose.npm-net.yml -f docker-compose.from-fork.yml up -d --no-deps --force-recreate openmu-startup` | ||
|
|
||
| Notes | ||
| - `src/Startup/Dockerfile` installs ICU (`icu-libs`, `icu-data-full`) and sets `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false` to enable real cultures on Alpine. | ||
| - If you are behind a proxy (NPM/Cloudflare), enable WebSockets for `/_blazor` and avoid orange cloud (DNS only) for the host. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| await this.SendMessageToPlayerAsync(sender, $"Chat bloqueado: quedan {(int)Math.Ceiling(remainingChatBan.TotalMinutes)} minuto(s).", MessageType.BlueNormal).ConfigureAwait(false); | ||
| } | ||
| else | ||
| { | ||
| await this.SendMessageToPlayerAsync(sender, $"Chat Ban: {(int)Math.Ceiling(remainingChatBan.TotalSeconds)} second(s) remaining.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| await this.SendMessageToPlayerAsync(sender, $"Chat bloqueado: quedan {(int)Math.Ceiling(remainingChatBan.TotalSeconds)} segundo(s).", MessageType.BlueNormal).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| await this.SendMessageToPlayerAsync(player, "No es posible formar party durante este evento.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| return; | ||
| } | ||
|
|
||
| if (toRequest.Party != null || toRequest.LastPartyRequester != null) | ||
| { | ||
| await this.SendMessageToPlayerAsync(player, $"{toRequest.Name} is already in a party.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| await this.SendMessageToPlayerAsync(player, $"{toRequest.Name} ya está en un party.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| return; | ||
| } | ||
|
|
||
| if (isPartyMember) | ||
| { | ||
| await this.SendMessageToPlayerAsync(player, "You are not the Party Master.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| await this.SendMessageToPlayerAsync(player, "No eres el líder del party.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| return; | ||
| } | ||
|
|
||
| if (await toRequest.PlayerState.TryAdvanceToAsync(PlayerState.PartyRequest).ConfigureAwait(false)) | ||
| { | ||
| await this.SendPartyRequestAsync(toRequest, player).ConfigureAwait(false); | ||
| await this.SendMessageToPlayerAsync(player, $"Requested {toRequest.Name} for Party.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| await this.SendMessageToPlayerAsync(player, $"Has enviado una solicitud de party a {toRequest.Name}.", MessageType.BlueNormal).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| var message = $"Se inicia defensa propia por el ataque de {attacker.Name} a {defender.Name}!"; | ||
| await defender.InvokeViewPlugInAsync<IShowMessagePlugIn>(p => p.ShowMessageAsync(message, MessageType.BlueNormal)).ConfigureAwait(false); | ||
| await attacker.InvokeViewPlugInAsync<IShowMessagePlugIn>(p => p.ShowMessageAsync(message, MessageType.BlueNormal)).ConfigureAwait(false); | ||
| } | ||
|
|
||
| private async ValueTask EndSelfDefenseAsync(Player attacker, Player defender) | ||
| { | ||
| var message = $"Self defense of {defender.Name} against {attacker.Name} diminishes."; | ||
| var message = $"La defensa propia de {defender.Name} contra {attacker.Name} finaliza."; | ||
| await defender.InvokeViewPlugInAsync<IShowMessagePlugIn>(p => p.ShowMessageAsync(message, MessageType.BlueNormal)).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| } | ||
|
|
||
| await player.GameContext.SendGlobalMessageAsync($"{selectedCharacter.Name} entered the game.", MessageType.BlueNormal).ConfigureAwait(false); | ||
| await player.GameContext.SendGlobalMessageAsync($"{selectedCharacter.Name} entró al juego.", MessageType.BlueNormal).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This global message is hardcoded in Spanish. Please use the localization service to allow for translation.
var message = player.GameServerContext.Localization.GetString("Server_Message_PlayerEnteredGame", selectedCharacter.Name);
await player.GameContext.SendGlobalMessageAsync(message, MessageType.BlueNormal).ConfigureAwait(false);| this._classNumber = classNumber; | ||
| this._minimumLevel = minimumLevel; | ||
| this._warningMessage = $"Couldn't unlock {className}, because it couldn't be found in the configuration."; | ||
| this._warningMessage = $"No se pudo desbloquear {className}, porque no se encontró en la configuración."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Thanks for this contribution. Because it's so big I'll need some time to review it. I'll probably need to divide it into several smaller PRs, like one per feature. Just leave this PR open, so I can keep track of which files I already reviewed. |
Summary: This PR contributes the main improvements from the OpenMU‑S2 fork: adds English/Spanish localization, enhances the Admin Panel (including log tailing), refactors and fixes summon behavior, adds event data (Rena, Golden Archer, White Wizard), improves logging and Docker setups, and updates docs and tests.
Highlights by area:
Localization: New src/Localization project with JSON resources (strings.en.json, strings.es.json); Admin Panel localization with LanguageSelector and localized components/files under src/Web/AdminPanel/Localization.
Admin Panel: New pages and widgets (e.g., Craftings.razor, LogTail.razor, LogTailWidget.razor), improved tables and navigation, style tweaks, and UX fixes across multiple .razor pages.
Summon logic: Refactor to allow a single configuration plug‑in to customize all summons; fix self‑defense interaction with own summon; expanded logging for summon behavior.
Gameplay fixes: Multiple fixes in crafting, inventory, trade, dup prevention, zen handling, NPC interactions, and attribute calculations across src/GameLogic/*.
Event/data updates: Add/update data and initialization for Rena global drop, Golden Archer, and White Wizard; adjust UpdateVersion to include new updates and keep upstream values intact.
Plug‑ins and commands: New AutoBroadcastMessagesPlugIn; additional chat command capabilities (e.g., item stack command args and handling).
Networking: Packet updates for item crafting result and related server‑to‑client definitions.
Startup & logging: In‑memory log buffer/sink and console logging utilities under src/Startup/Logging; Program.cs and appsettings.json adjustments to surface logs and improve diagnostics.
Docker: Multiple compose variants and overlays for local/public/LAN and with/without NGINX (deploy/all-in-one/*), plus admin‑port override.
Docs: Updated README.md, QuickStart.md, and added docs for events and summon/logging (docs/*).
Solution: Added src/Localization/MUnique.OpenMU.Localization.csproj and updated src/MUnique.OpenMU.sln.
Tests: Added tests/MUnique.OpenMU.Tests/SelfDefensePlugInTest.cs, updated packet tests.
Breaking changes / migration:
How to test:
Size and scope: