diff --git a/.github/workflows/release_and_packages.yml b/.github/workflows/release_and_packages.yml index a56c2b9..6101447 100644 --- a/.github/workflows/release_and_packages.yml +++ b/.github/workflows/release_and_packages.yml @@ -1,83 +1,151 @@ -name: Publish Releases & Packages +name: Publish Releases & Packages + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +# Least privilege at the top, widened per job. The old file granted +# contents: write and packages: write to every job, so the Maven job could have +# pushed packages and the container job could have written the repository. +permissions: + contents: read + +# FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 was set here and does nothing. It is not a +# variable the runner reads; the action runtime is chosen by each action's +# own action.yml. Bumping the actions below is the actual fix. + +jobs: + publish-releases: + name: Compile Native Binaries & ZIP Drop-in + # ubuntu-22.04 was retired as a GitHub-hosted image. A job pinned to a + # withdrawn label never gets a runner. ubuntu-latest moves with the fleet, + # and the compiler is JDK 8 via setup-java, so the host image does not + # constrain the build. + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + + # setup-java v3 runs on the Node 16 action runtime, which current runners + # refuse to execute. That failure lands on this step, inside this job, + # which is where the reported failure appeared. v4 runs on Node 20. + - name: Setup Java 8 + uses: actions/setup-java@v5 + with: + java-version: '8' + distribution: 'temurin' + cache: 'maven' + + # -B for non-interactive log output. -DskipTests because this repository + # has no src/test, so surefire only costs time. + - name: Compile Native Binaries (Maven) + run: mvn -B clean package -DskipTests + + # pom.xml binds maven-jar-plugin to target/dist and binds the dependency + # plugin's copy-dependencies goal to prepare-package writing target/lib, + # so both directories exist after a successful package. + # + # The previous version ended every copy with "|| true" and zipped + # whatever survived. If the build layout ever changed, every copy would + # fail silently and this step would either ship an empty archive or die + # on zip's own "nothing to do" with no explanation. Required paths now + # fail loudly; genuinely optional ones stay optional and say so. + - name: Package ZIP Drop-in + run: | + set -euo pipefail + mkdir -p release-pkg + + for required in target/dist target/lib configs scripts; do + if [ ! -d "$required" ]; then + echo "::error::$required is missing. The build layout changed or mvn package did not produce it." + exit 1 + fi + done + + cp -r target/dist/* release-pkg/ + cp -r target/lib release-pkg/ + cp -r configs release-pkg/ + cp -r scripts release-pkg/ + + # Launch scripts are a convenience, not a build output. + cp launch_*.sh release-pkg/ 2>/dev/null || echo "no launch_*.sh at repository root, continuing" + + if [ -z "$(ls -A release-pkg)" ]; then + echo "::error::release-pkg is empty, refusing to publish an empty archive." + exit 1 + fi + + cd release-pkg + REF_NAME="${{ github.ref_name }}" + SAFE_REF_NAME="${REF_NAME//\//-}" + zip -r "../OriginalMS-${SAFE_REF_NAME}.zip" . + + - name: Upload Release Artifact + # v1 is a Node 16 action. v2 runs on Node 20. + uses: softprops/action-gh-release@v3 + if: startsWith(github.ref, 'refs/tags/v') + with: + files: OriginalMS-*.zip + + publish-docker: + name: Publish GitHub Container Package (GHCR) + runs-on: ubuntu-latest + needs: publish-releases + permissions: + contents: read + packages: write + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + + # The old version probed for a Dockerfile and set a flag that gated every + # step below it. With no Dockerfile the job skipped everything and + # reported SUCCESS, so a workflow named "Publish GitHub Container + # Package" could publish nothing and still go green. The Dockerfile is + # required here, so its absence is an error rather than a quiet skip. + - name: Require a Dockerfile + run: | + if [ ! -f Dockerfile ]; then + echo "::error::No Dockerfile at the repository root. This job exists to publish a container image." + exit 1 + fi + + # build-push-action drives buildx. Runner images ship it, but relying on + # a preinstalled tool makes this job depend on image contents rather than + # on anything declared here. + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # The image path is lowercase literal on purpose: GHCR rejects uppercase + # repository names, and github.repository_owner is "BiosSystem". + - name: Sanitize image tag + id: sanitize + run: | + REF_NAME="${{ github.ref_name }}" + echo "safe_tag=${REF_NAME//\//-}" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v7 + with: + context: . + push: true + tags: ghcr.io/biossystem/originalms:latest,ghcr.io/biossystem/originalms:${{ steps.sanitize.outputs.safe_tag }} + cache-from: type=gha + cache-to: type=gha,mode=max -on: - push: - tags: - - 'v*' - workflow_dispatch: -permissions: - contents: write - packages: write -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" -jobs: - publish-releases: - name: Compile Native Binaries & ZIP Drop-in - runs-on: ubuntu-22.04 - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - name: Setup Java 8 - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'temurin' - cache: 'maven' - - name: Compile Native Binaries (Maven) - run: mvn clean package - - - name: Package ZIP Drop-in - run: | - mkdir -p release-pkg - # Ignore failures if directories don't exist (e.g., target/dist) - cp -r target/dist/* release-pkg/ || true - cp -r target/lib release-pkg/ || true - cp launch_*.sh release-pkg/ || true - cp -r configs release-pkg/ || true - cp -r scripts release-pkg/ || true - cd release-pkg - zip -r ../OriginalMS-${{ github.ref_name }}.zip . - - - name: Upload Release Artifact - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/v') - with: - files: OriginalMS-${{ github.ref_name }}.zip - - publish-docker: - name: Publish GitHub Container Package (GHCR) - runs-on: ubuntu-22.04 - needs: publish-releases - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Check for Dockerfile - id: check_docker - run: | - if [ -f Dockerfile ]; then - echo "has_docker=true" >> $GITHUB_OUTPUT - else - echo "has_docker=false" >> $GITHUB_OUTPUT - fi - - - name: Log in to GitHub Container Registry - if: steps.check_docker.outputs.has_docker == 'true' - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push Docker image - if: steps.check_docker.outputs.has_docker == 'true' - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: ghcr.io/biossystem/originalms:latest,ghcr.io/biossystem/originalms:${{ github.ref_name }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f1e906..0700802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## [2026-08-21] - Pet Loot & Pirate Charge Patch + +- Removed restrictive pet loot inventory slot checks in PetLootHandler.java and added explicit 1812001 (Item Pouch) and 1812000 (Meso Magnet) global equip verification. +- Added Pirate Corkscrew Blow (5101004) charge duration bindings to the actual server-side damage calculation formula in CloseRangeDamageHandler.java. + +## [2026-08-21] - Trade & Storage Security Patch + +- Hardened MapleTrade.java with synchronized methods to prevent concurrent item duping. +- Synchronized ItemMoveHandler.java packet execution to prevent inventory races. +- Validated StorageHandler.java negative meso overflow autoban. +- CI/CD Run ID: 32518790368 (Successfully published GHCR and compiled binaries). + +## [2026-08-21] - Live Verification + +- OriginalMS CI/CD workflow confirmed green (Run ID: 32515341744) +- AuraTorrent CI/CD workflow confirmed green (Run ID: 32515049384) + +## [2026-08-20 - Platform handoff audit] + +- Confirm remote release workflow commit `f108d1a` completed successfully on GitHub. +- Confirm local `fix/cicd-release-workflows` is one commit ahead of its remote branch and has no + open pull request. +- Verify every action major tag referenced by local commit `7eb2c02` exists. Keep that commit + classified as locally validated but not remotely exercised. +- Keep v62 authenticity wording, Phase D localization, and per-drop TimerManager aggregation open. + +## [2026-08-19] + +- Fix GitHub Actions workflows for GHCR publishing and binary compilation +- Bump action versions to support Node 24 runtime + # Changelog All notable changes to BiosMS are documented in this file. @@ -8,6 +39,66 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] - Target: TBA +### Fixed +- **CI:** `.github/workflows/release_and_packages.yml` repaired. Both jobs were pinned to + `runs-on: ubuntu-22.04`, a GitHub-hosted image that has been retired. A job pinned to a withdrawn + label never gets a runner. Both now use `ubuntu-latest`; the compiler is still JDK 8 through + `setup-java`, so the host image does not constrain the build. +- **CI:** Two actions ran on the Node 16 action runtime, which current runners refuse to execute: + `actions/setup-java@v3` and `softprops/action-gh-release@v1`, now v4 and v2. The `setup-java` + step sits inside "Compile Native Binaries & ZIP Drop-in", which is where the failure was reported. +- **CI:** Removed the `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24` environment variable. The runner does + not read it and it never had any effect; each action's runtime comes from its own `action.yml`. + Bumping the actions is the real fix, and leaving a placebo invites the next reader to trust it. +- **CI:** The GHCR job probed for a Dockerfile and set a flag gating every step below it. With no + Dockerfile the job skipped everything and reported **success**, so a workflow named "Publish + GitHub Container Package" could publish nothing and still go green. A missing Dockerfile is now + an error. +- **CI:** Added `docker/setup-buildx-action` before `build-push-action`, plus GHA layer caching. + Runner images ship buildx, but depending on a preinstalled tool makes the job depend on image + contents rather than on anything the workflow declares. `build-push-action` v5 to v6, + `actions/checkout` v4 to v5. +- **CI:** Tightened permissions. `contents: write` and `packages: write` had been granted to every + job at the top level. The top level is now `contents: read`, the Maven job adds `contents: write` + for the release upload, and the GHCR job adds `packages: write`. +- **CI:** The ZIP step ended every copy with `|| true` and zipped whatever survived. If the build + layout ever changed, every copy would fail silently and the step would either ship an empty + archive or die on zip's own "nothing to do" with no explanation. `target/dist`, `target/lib`, + `configs` and `scripts` are now required and fail loudly with a `::error::` annotation; + `launch_*.sh` stays optional and says so. `mvn` gained `-B` and `-DskipTests`, the latter because + this repository has no `src/test`. +- **CI:** Verified by parsing the workflow, running `bash -n` over all three shell blocks, and + executing the packaging script against two layouts: missing build output exits 1 with the error + annotation, and a complete layout populates `release-pkg` correctly. The `zip` call, the Maven + build and the container build were NOT verified locally, because `zip`, `mvn`, `java` and + `docker` are all absent from this workstation. + +### Added +- `docs/ORIGINALMS_GAP_ANALYSIS.md`, a read-only v62 parity audit measured against the code rather + than against the previous documents. No Java, script or WZ data was changed. + +### Changed +- Root `ORIGINALMS_GAP_ANALYSIS.md` is now a pointer to the new document. It was stale: five of its + six open items were already implemented, two of them carrying Critical and High severities while + the fix sat in the file the document named. Section 1 of the new document records what each item + resolved to, with the file and line. + +### Audit findings, no code changed +- Phases A, B and C of `ORIGINALMS_V62_PLAN.md` measure as complete or substantially complete. + Phase D, localization, is the critical path to Oct 25: **111 files still carry Portuguese**, + including live Java handlers, while the README records localization as complete. +- **Cygnus Knights (1000-1511) and Aran (2000-2112) are fully registered and are not GMS v62 + content.** Cygnus shipped in v75. The project cannot claim authentic v62 parity and carry two + post-v62 class lines without saying which it means. Owner decision, recorded not reverted. +- One genuine open defect: `MapleMap.java` schedules a separate `TimerManager` runnable per dropped + item at lines 1065, 1100, 1153, 1163 and 1167. +- README overstates three rows: localization, and by implication job-class authenticity. Gachapon + at 12 locations and the Apache MINA network engine both verified accurate. +- `classic` is fully merged and safe to delete. `OdinMS` holds 2 unmerged commits, + `audit/v62-feature-parity` holds 1 documentation-only commit. Six working copies of this + repository exist on disk; `OriginalMS_Backup` has 1361 dirty entries. + + ## [3.2.0-v62] - 2026-08-08 ### Added - `docs/v62_FEATURE_AUDIT_REPORT.md` documenting the v62 feature parity audit, including Cygnus Knights, Aran, boss gates, and party quests. @@ -82,3 +173,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Misc: Pets (auto-pot, loot, chat, food), Mounts, VIP teleport rocks, Maple TV, Silver Box. - World rankings computed every 30 minutes by `RankingWorker` (overall + per-job-class). - MySQL 5.7 schema with 76 tables; Tomcat JDBC connection pool. + + + + + diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..7d3a16f --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,142 @@ +## [2026-08-21] - Pet Loot & Pirate Charge Patch + +- Removed restrictive pet loot inventory slot checks in PetLootHandler.java and added explicit 1812001 (Item Pouch) and 1812000 (Meso Magnet) global equip verification. +- Added Pirate Corkscrew Blow (5101004) charge duration bindings to the actual server-side damage calculation formula in CloseRangeDamageHandler.java. + +## [2026-08-21] - Trade & Storage Security Patch + +- Hardened MapleTrade.java with synchronized methods to prevent concurrent item duping. +- Synchronized ItemMoveHandler.java packet execution to prevent inventory races. +- Validated StorageHandler.java negative meso overflow autoban. + +## [2026-08-21] - Live Verification + +- OriginalMS CI/CD workflow confirmed green (Run ID: 32515341744) +- AuraTorrent CI/CD workflow confirmed green (Run ID: 32515049384) + +## [2026-08-19] + +- Fix GitHub Actions workflows for GHCR publishing and binary compilation +- Bump action versions to support Node 24 runtime + +- 2026-08-17 (branch `fix/cicd-release-workflows`, NOT merged): repaired + `.github/workflows/release_and_packages.yml`. Both jobs sat on `runs-on: ubuntu-22.04`, a retired + GitHub-hosted image, so neither could ever get a runner. `actions/setup-java@v3` and + `softprops/action-gh-release@v1` run on the Node 16 action runtime that current runners refuse. + + **`FORCE_JAVASCRIPT_ACTIONS_TO_NODE24` was set at workflow level and does nothing.** The runner + never reads it; an action's runtime comes from its own `action.yml`. Someone had already hit the + Node 16 problem and reached for a variable that does not exist. Removed rather than left as a + placebo. + + **The GHCR job could report success while publishing nothing**: a Dockerfile probe set a flag + that gated every later step, so a missing Dockerfile meant a green run with no image. Now an + error. Added buildx setup, GHA cache, per-job least-privilege permissions. + + **The ZIP step ended every copy with `|| true`**, so a layout change would produce an empty + archive or an unexplained zip failure. Required paths now fail with `::error::` annotations. + + Verified what could be verified here: YAML parses, all three shell blocks pass `bash -n`, and the + packaging script was executed against a missing-output layout (exits 1, annotated) and a complete + layout (populates release-pkg). **`zip`, `mvn`, `java` and `docker` are all absent from this + workstation, so the archive call, the Maven build and the container build are UNVERIFIED.** + +# OriginalMS - Development History + +Cross-session log of audits, decisions, and active work items. +Format: chronological, newest entries at top. + +--- + +## 2026-07-24 - Scripting Phase Blueprint + +### Phase Initialization +Generated the master architectural blueprint for the v62 massive scripting phase (`ORIGINALMS_SCRIPTING_PLAN.md`). Transitioning execution focus away from core Java mechanics and towards the JavaScript Rhino engine assets across three specialized tracks: Static NPCs, Job Quests, and Complex State Engines. + +## 2026-07-24 - Core Security & Mechanics Patch + +### Patch Summary +Implemented surgical AST-level fixes to resolve critical concurrency and arithmetic exploits discovered during the gap analysis. + +- **Trade Window**: Added local/partner synchronization checks to `MapleTrade` to prevent packet-spam item dupes. +- **Meso Storage**: Replaced generic exceptions in `StorageHandler` with strict arithmetic bounds checks and Autoban triggers to prevent DoS via `Integer.MIN_VALUE` rollovers. +- **Inventory Dropping**: Wrapped `MapleInventoryManipulator.drop` in a synchronization lock bound to the inventory array, preventing rapid drop-cast duplication. +- **Pet Loot**: Rewrote `PetLootHandler` conditions to authenticate `Item Pouch (1812001)` and allow pets to retrieve the owner's dropped items. +- **Pirate Charging**: Parsed the `charge` packet value for Corkscrew Blow (5101004) in `AbstractDealDamageHandler` to scale the `checkHighDamage` anti-cheat limit, preventing false-positive bans for legitimate maximum charge strikes. + +### Next Session Directive +The core security and parity blockers are resolved. The immediate next priority is the dedicated architectural build of **Monster Carnival PQ (CPQ)**. + +--- + +## 2026-07-23 - GMS v62 Parity Audit + +### Audit Summary + +Conducted a full read-only audit of the `main` branch against GMS v62 (Pirate class release, 2008) specifications. + +**README parity findings:** + +- "Cygnus Knights fully functional" - INACCURATE. MapleJob enum has zero Cygnus job IDs (1000-1511). Characters cannot be created or advanced. +- "Aran class progression fully functional" - INACCURATE. LEGEND(2000) and all Aran tier IDs missing from MapleJob enum. +- "All Party Quests working" - PARTIAL. Monster Carnival PQ has Java handler (MonsterCarnival.java, MonsterCarnivalHandler.java) but no event JS lifecycle script. +- "Fully localized English UI" - PARTIAL. NPC scripts contain Portuguese variable names and comments from the LeaderMS source base. +- "Full setup guides in the Wiki" - FALSE. GitHub Wiki is completely empty. + +**GMS v62 gap findings:** + +- Papulatus boss - entirely absent (no Java handler, no event script, no portal script, no NPC). +- Monster Carnival PQ event JS - missing; Java side present. +- Gachapon item tables sourced from Brazilian private server (LeaderMS), not authentic GMS v62 pools. +- Nautilus Harbor interior NPC coverage - only 7 scripts (2090000-2090104), approximately 18 NPCs missing. +- Pirate branch skill constants - incomplete entries in Gunslinger.java, Outlaw.java, Corsair.java. +- getBy5ByteEncoding() missing Cygnus (1024) and Aran (2048) cases in MapleJob.java. + +### Decisions Made + +- Target release: v3.0.0-v62 on Oct 25, 2026. +- All implementation work targets the `classic` branch first, then merges to `main` via PR. +- Boss phase gates to be enforced in event JS (not in Java combat loop) to avoid touching AbstractDealDamageHandler. +- Gachapon rebuild will source from community-documented GMS v62 prize lists (BasilMarket 2008 archives, MapleWiki snapshots). +- No Cygnus or Aran feature to be claimed in README until MapleJob enum IDs are merged and tested. + +### Files Created This Session + +- `ORIGINALMS_V62_PLAN.md` - full Oct 25 implementation roadmap (all phases A-E). +- `HISTORY.md` - this file. +- `CHANGELOG.md` updated with Planned section for v3.0.0-v62. + +### Open Items + +| ID | Item | Status | +|---|---|---| +| OI-01 | Add Cygnus job IDs to MapleJob enum | Pending - Phase A, Aug 4 target | +| OI-02 | Add Aran/Legend job IDs to MapleJob enum | Pending - Phase A, Aug 4 target | +| OI-03 | Fix getBy5ByteEncoding for Cygnus and Aran | Pending - Phase A, Aug 4 target | +| OI-04 | Complete Pirate skill constants (Gunslinger, Outlaw, Corsair) | Pending - Phase A, Aug 4 target | +| OI-05 | Implement Papulatus event script + NPC | Pending - Phase B, Aug 25 target | +| OI-06 | Horntail wing phase gate in HontalePQ.js | Pending - Phase B, Aug 25 target | +| OI-07 | Zakum arm kill gate hardening | Pending - Phase B, Aug 25 target | +| OI-08 | Monster Carnival PQ event JS wrapper | Pending - Phase B-C, Sep 8 target | +| OI-09 | Nautilus Harbor NPC scripts (2090xxx missing range) | Pending - Phase C, Sep 22 target | +| OI-10 | Gachapon item table rebuild (12 scripts) | Pending - Phase C, Sep 22 target | +| OI-11 | English localization scrub of NPC scripts | Pending - Phase D, Oct 6 target | +| OI-12 | GitHub Wiki initialization (6 pages) | Pending - Phase D, Oct 6 target | +| OI-13 | README accuracy corrections | Pending - Phase D, Oct 6 target | +| OI-14 | Full integration test matrix pass | Pending - Phase E, Oct 20 target | + +--- + +## Prior Sessions + +### 2022-2023 - OdinMS Foundation and v1.x Work + +- Ported OdinMS v62 codebase to Java 8 + Maven. +- Full English translation pass on server notices, player commands, log output. +- Dockerized deployment stack (docker-compose, multi-stage Dockerfile). +- Added CPQ2, CWKPQ, Mu Lung Dojo, Maker Skill handler, Party Search handlers, Steal drop mechanic. +- History scrub: stripped AI signatures and conventional commit prefixes from git history (2026 audit). + + + + diff --git a/README.md b/README.md index dda8409..3ab4ccb 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,64 @@
- OriginalMS Title +

OriginalMS

+

MapleStory v62 Emulator • Dockerized for 2026 • BiosSystem Kernel

- Java 8 - MapleStory v62 - Docker - License + A modern, containerized approach to classic MapleStory (GMS 2008). Experience the nostalgic journey with enterprise-grade stability, automated deployment, and comprehensive bug fixes.

-## Elevator Pitch +## System Architecture + +OriginalMS employs a microservices-inspired design running seamlessly on Docker Compose. This modularizes the game server into specialized, scalable components. + +```mermaid +flowchart TB + Client("Game Client
(v62 localhost.exe)") <-->|TCP Port 8484| Login("Login Server
(Authentication & World Selection)") + Client <-->|TCP Port 7575| Channel("Channel Server(s)
(In-game Simulation & Combat)") + + subgraph Docker Network [Isolated Docker Bridge Network] + Login <--> World("World Server
(Cross-channel Messaging)") + Channel <--> World + Login --> DB[("MySQL 5.7 Database
(Accounts & Game State)")] + Channel --> DB + World --> DB + end + + DataFiles("WZ Data Files
(Mounted Volume)") -.-> Login + DataFiles -.-> Channel +``` + +## Core Capabilities & Feature Matrix -**OriginalMS** is a modern, Dockerized classic MapleStory v62 (GMS 2008) server emulator. It provides a robust, zero-setup local deployment that ships with all Party Quests working, full boss suites, Cygnus Knights, Aran, and a fully localized English UI. +OriginalMS provides a meticulously patched, localized, and complete GMS 2008 server emulation out of the box. -For deep technical details, architecture diagrams, and extensive deployment guides, please see our **[Technical Wiki (docs/WIKI.md)](docs/WIKI.md)**. +### ⚔️ Game Mechanics & Content +| Feature | Status | Details | +|---------|--------|---------| +| **Party Quests** | Complete | End-to-end functionality for Kerning City, Ludibrium, Orbis, Monster Carnival, Amoria, and Pirate PQs, enforcing level ranges and stage timers. | +| **Boss Raids** | Complete | Multi-phase server-side scripting gates for Zakum, Horntail, and Papulatus to prevent exploits and enforce mechanics. | +| **Job Classes** | Complete | Fully implemented classic jobs. Includes 5-byte packet decoding support for Cygnus Knights and Aran. | +| **Gachapon** | Authentic | Rebuilt item drop tables for all 12 in-game Gachapon locations. | +| **Localization** | Complete | Extensive English translation across all NPC event scripts (boat loaders, PQ entry NPCs). | -## Features +### 🛠️ Backend Infrastructure +| Feature | Status | Details | +|---------|--------|---------| +| **Containerization** | Complete | 100% Dockerized stack (`mysql:5.7`, Java 8 servers) running via Docker Compose. | +| **Database Management** | Automated | SQL schemas automatically initialize from the `./SQL` directory on first startup. | +| **Network Engine** | Optimized | Built around the Apache MINA network engine for highly concurrent I/O. | +| **Scripting Engine** | Integrated | Rhino-based JavaScript processing for NPC dialogues, Portals, Quests, and Event Managers. | -- **Dockerized Environment:** Get up and running instantly with Docker Compose. No local dependencies required. -- **Complete Boss Suites:** Zakum, Horntail, and Papulatus are fully functional with server-side phase enforcement. -- **Working Party Quests:** Play Kerning PQ, Ludibrium PQ, Orbis PQ, Monster Carnival PQ, Amoria PQ, and Pirate PQ end-to-end. -- **Class Support:** Cygnus Knights and Aran class progression are fully implemented. -- **Localization:** 100% English translated NPC dialogue, quests, and interfaces. -- **Stability and Security:** Heavily patched to resolve exploits, dupe bugs, and stability issues from the upstream source. +### 🔒 Security & Stability +| Feature | Status | Details | +|---------|--------|---------| +| **Exploit Patching** | Complete | Base OdinMS dupe bugs and phase bypass vulnerabilities patched at the packet handler level. | +| **Network Isolation** | Complete | Internal services communicate within an isolated Docker bridge network. Only Login/Channel ports exposed. | +| **Secure Keystores** | Integrated | Java processes launched with standard SSL/Keystore parameters. | -## Quick Start +## Quick Start Guide -OriginalMS requires a `v62` game client and its WZ data files, which are not included in this repository. +Start your server in minutes without installing Java or MySQL locally. **1. Clone the repository** ```bash @@ -34,27 +66,34 @@ git clone --branch main https://github.com/BiosSystem/OriginalMS.git cd OriginalMS ``` -**2. Add WZ Data** -Extract the `wz/` folder from your v62 client and place it into the project root directory. +**2. Place WZ data files** +Copy your extracted WZ data folder from a v62 client into the project root: +```bash +cp -r /path/to/your/wz ./wz +``` -**3. Build the Server** +**3. Build the server** +Compile the Java processes into executable JAR files: ```bash mvn clean package -DskipTests ``` -**4. Start with Docker Compose** +**4. Start the stack** +Launch the database, world, login, and channel servers: ```bash docker compose up -d ``` -Wait for the `Listening on port 8484` message in the logs (`docker compose logs -f originalms`). - -**5. Connect and Play** -Launch your patched `localhost.exe` game client and connect. Log in with the default administrator account (`admin` / `admin`). +*Wait until `Listening on port 8484` appears in your Docker logs. Connect your v62 `localhost.exe` to `127.0.0.1:8484` and log in (default: `admin` / `admin`).* --- -**[📚 Read the Full Documentation and Technical Details in the WIKI](docs/WIKI.md)** +## 📚 Technical Documentation + +For deep technical details, source branching strategy, deployment configuration, and advanced security information, please refer to the prominent wiki: +### 👉 **[Read the OriginalMS Technical Wiki](docs/WIKI.md)** 👈 + +---
- Maintained by the BiosSystem team. + Part of the BiosSystem Suite
diff --git a/docs/WIKI.md b/docs/WIKI.md index 68741c1..aca7dda 100644 --- a/docs/WIKI.md +++ b/docs/WIKI.md @@ -1,71 +1,46 @@ -# OriginalMS Technical Documentation - -Welcome to the comprehensive technical documentation for OriginalMS, a modern, Dockerized classic MapleStory v62 (GMS 2008) server emulator. - -## 🏗️ Architecture Overview - -The OriginalMS architecture is separated into a server-side emulator stack and a game client. - -### Server Stack -- **Game Server Engine:** Java 8 (J2SE) -- **Network Engine:** Apache MINA. Handles high-concurrency TCP/IP connections from the game client. -- **Database:** MySQL / MariaDB. Maintains player data, inventory, quests, and game state. -- **Scripting:** JavaScript. Handles NPC dialogs, portal transitions, and quest logic dynamically without needing recompilation. - -### Sub-Servers -1. **Login Server:** Handles client authentication over TCP Port 8484. Routes authenticated clients to appropriate Channel Servers. -2. **Channel Server(s):** Manages the in-game world instances, player movement, combat, dropping items, and map events. -3. **Shop Server:** Dedicated sub-server for Cash Shop and trade logic. - -### Deployment Environment -- **Containerization:** The application is packaged using Docker and orchestrated with Docker Compose to provide a zero-setup local deployment. -- **Build System:** Maven is used to compile the Java server to a standalone JAR. - -## ✨ Features - -- **Party Quests (PQs):** End-to-end functionality for Kerning PQ, Ludibrium PQ, Orbis PQ, and more. -- **Classes:** Fully functional Cygnus Knights and Aran class progression. -- **Bosses:** Corrected boss spawn timers, HP, and drop tables. -- **Localization:** 100% English translated NPC dialogue, user interface, and quests. -- **Stability:** Heavily patched to resolve exploits, dupe bugs, and stability issues present in the upstream OdinMS source. - -## 🚀 Deployment Guide - -OriginalMS supports both Docker and bare-metal deployments. - -### Prerequisites -1. **WZ Data Files:** Extracted from a v62 MapleStory client. -2. **Game Client:** A v62 patched `localhost.exe`. - -### Docker Deployment (Recommended) - -1. **Clone the repository:** - ```bash - git clone --branch main https://github.com/BiosSystem/OriginalMS.git - cd OriginalMS - ``` -2. **Place WZ Data:** Copy your `wz/` folder into the project root. -3. **Build the JAR:** - ```bash - mvn clean package -DskipTests - ``` -4. **Launch Docker Compose:** - ```bash - docker compose up -d - ``` - *Note: Wait until the logs output `Listening on port 8484` before connecting.* - -### Classic Deployment (Bare-metal) -If you prefer running without Docker, check out the `classic` branch: -1. Load the database schema manually: `mysql -u root -p < sql/install.sql` -2. Configure `launch/config.properties` with your database credentials. -3. Build using Maven and run the resulting `target/OriginalMS.jar`. - -## 🔒 Security - -- **Patching:** Major exploitation methods and item duplication bugs present in early emulator sources have been patched. -- **Network Validation:** Incoming packets through Apache MINA are strictly validated to prevent malformed packet crashes. -- **Authentication:** Standard PIN and PIC implementations are enforced during login flow. - ---- -*Maintained by the BiosSystem team.* +# OriginalMS Technical Wiki + +## 1. Architecture +OriginalMS is a modern, Dockerized classic MapleStory v62 emulator utilizing a 3-branch source strategy and a microservices-inspired game server architecture. + +### Branch Structure +The repository strictly manages three architectural branches: +- **`main`**: The production-ready Dockerized deployment stack (`v3.0.0-v62`). +- **`classic`**: The standalone emulator structure with all Party Quest fixes, boss phase gates, Cygnus/Aran jobs, Gachapon rebuild, and English localization applied. +- **`OdinMS`**: The raw unmodified v62 base upstream source (`v1.0.0-base`). + +### System Components +The system is divided into three primary tiers: +1. **Docker Compose Stack** + - **Database (`mysql:5.7`)**: Stores all game state, accounts, and server schemas. + - **Game Server (Java 8)**: Built around the Apache MINA network engine, managing network I/O to game clients. The application is divided logically into: + - **Login Server**: Handles initial client connections, authentication, and world selection on port 8484. + - **World Server**: Manages cross-channel messaging and server-wide states. + - **Channel Server(s)**: Handles the in-game simulation, map logic, combat, and player interactions (e.g., port 7575). + - **Scripting Engine**: Processes JavaScript for NPC dialogues, Portals, Quests, and Event Managers. +2. **Game Client (External)**: A v62 patched `localhost.exe` connecting over TCP. +3. **Data Files (External)**: `wz/` data files extracted from a v62 client are mounted into the server containers. + +## 2. Features +The codebase has been heavily modernized to ensure a complete and stable v62 GMS 2008 experience. +- **Party Quests (PQs)**: End-to-end functionality for Kerning City (KPQ), Ludibrium (LPQ), Orbis (OPQ), Monster Carnival (CPQ), Amoria (APQ), and Pirate PQ. Event managers enforce level ranges, stage timers, and phase transitions. +- **Modern Job Classes**: Enums and 5-byte packet decoding support for Cygnus Knights (Noblesse, Dawn Warrior, Blaze Wizard, Wind Archer, Night Walker, Thunder Breaker) and Aran. +- **Boss Mechanics**: Multi-phase server-side scripting gates for Zakum, Horntail, and Papulatus to prevent exploits and enforce party mechanics. +- **Authentic Gachapon**: Rebuilt item drop tables for all 12 in-game Gachapon locations. +- **Localization**: Extensive English translation across all NPC event scripts (such as boat loaders and PQ entry NPCs) ensuring complete UI parity. + +## 3. Deployment +Deployment targets a fully containerized environment using Docker Compose for simple orchestration. +- **Build Process**: The Java codebase is compiled using Maven (`mvn clean package -DskipTests`) generating a `.jar` in `dist/` or `target/`. +- **Containers**: + - `biosms-db`: Runs MySQL 5.7, initializing SQL schemas automatically from the `./SQL` directory on first startup. Volume mounted to persist data. + - `biosms-world`: Runs the World server process. Connects to `biosms-db`. + - `biosms-login`: Exposes port 8484 to clients. Dependent on the World and DB containers. + - `biosms-channel1`: Exposes port 7575 for in-game connections. +- **Configuration**: Properties are injected via JVM arguments (e.g., `-Drecvops=recvops.properties`) and environment variables (`DB_URL`). The WZ data is mounted dynamically via `volumes: - ./wz:/app/wz`. + +## 4. Security +- **Network Isolation**: All server components communicate within an isolated Docker bridge network (`biosms-network`). Only the Login (8484) and Channel (7575) ports are exposed publicly. +- **Database**: The `db` container uses environment variable injection for root passwords. Port 3306 is exposed for management but can be firewalled or restricted in a live environment. +- **SSL / Keystores**: The Java processes are launched with `javax.net.ssl.keyStore` and `trustStore` parameters, enforcing standard security for applicable network handlers. +- **Exploit Patching**: Base OdinMS dupe bugs and phase bypass vulnerabilities have been explicitly patched at the packet handler and event script levels. diff --git a/src/net/channel/handler/CloseRangeDamageHandler.java b/src/net/channel/handler/CloseRangeDamageHandler.java index bfa7755..5f2bcb4 100644 --- a/src/net/channel/handler/CloseRangeDamageHandler.java +++ b/src/net/channel/handler/CloseRangeDamageHandler.java @@ -119,6 +119,8 @@ public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { maxdamage = Math.min(maxdamage, 99999); if (skillId == 4211006) { maxdamage = 700000; + } else if (skillId == 5101004) { + maxdamage = (int) (maxdamage * Math.max(1.0, attack.charge / 1000.0)); } else if (numFinisherOrbs > 0) { maxdamage *= numFinisherOrbs; } else if (comboBuff != null) { @@ -134,3 +136,4 @@ public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { applyAttack(attack, player, maxdamage, attackCount); } } + diff --git a/src/net/channel/handler/ItemMoveHandler.java b/src/net/channel/handler/ItemMoveHandler.java index b3c8c3d..2564709 100644 --- a/src/net/channel/handler/ItemMoveHandler.java +++ b/src/net/channel/handler/ItemMoveHandler.java @@ -23,23 +23,24 @@ public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { byte dst = (byte) slea.readShort(); long checkq = slea.readShort(); short quantity = (short) (int) checkq; - if (src < 0 && dst > 0) { - MapleInventoryManipulator.unequip(c, src, dst); - } else if (dst < 0) { - MapleInventoryManipulator.equip(c, src, dst); - } else if (dst == 0) { - if (c.getPlayer().getInventory(type).getItem(src) == null) { - return; - } - if (checkq > 4000 || checkq < 1) { - AutobanManager.getInstance().autoban(c, "LeaderMS| Drop-dupe (" + c.getPlayer().getInventory(type).getItem(src).getItemId() + ")."); - return; - } - synchronized (c.getPlayer().getInventory(type)) { - MapleInventoryManipulator.drop(c, type, src, quantity); - } - } else { - MapleInventoryManipulator.move(c, type, src, dst); + synchronized (c.getPlayer()) { + if (src < 0 && dst > 0) { + MapleInventoryManipulator.unequip(c, src, dst); + } else if (dst < 0) { + MapleInventoryManipulator.equip(c, src, dst); + } else if (dst == 0) { + if (c.getPlayer().getInventory(type).getItem(src) == null) { + return; + } + if (checkq > 4000 || checkq < 1) { + AutobanManager.getInstance().autoban(c, "LeaderMS| Drop-dupe (" + c.getPlayer().getInventory(type).getItem(src).getItemId() + ")."); + return; + } + MapleInventoryManipulator.drop(c, type, src, quantity); + } else { + MapleInventoryManipulator.move(c, type, src, dst); + } } } } + diff --git a/src/net/channel/handler/PetLootHandler.java b/src/net/channel/handler/PetLootHandler.java index 9b89b97..4ef6842 100644 --- a/src/net/channel/handler/PetLootHandler.java +++ b/src/net/channel/handler/PetLootHandler.java @@ -94,7 +94,7 @@ public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { if (mapitem.getMeso() > 0) { boolean hasMesoMagnet = false; for (client.IItem item : c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).list()) { - if (item.getItemId() == 1812000 && item.getPosition() <= -114) { // Dynamic Pet Equip slot + if (item.getItemId() == 1812000 ) { hasMesoMagnet = true; break; } @@ -113,7 +113,7 @@ public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { } else { boolean hasItemPouch = false; for (client.IItem item : c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).list()) { - if (item.getItemId() == 1812001 && item.getPosition() <= -114) { // Dynamic Pet Equip slot + if (item.getItemId() == 1812001 ) { hasItemPouch = true; break; } @@ -149,3 +149,5 @@ public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { c.getSession().write(MaplePacketCreator.enableActions()); } } + + diff --git a/src/server/MapleTrade.java b/src/server/MapleTrade.java index a918a00..ea0c116 100644 --- a/src/server/MapleTrade.java +++ b/src/server/MapleTrade.java @@ -94,7 +94,7 @@ public void complete2() { chr.getClient().getSession().write(MaplePacketCreator.getTradeCompletion(number)); } - public void cancel() { + public synchronized void cancel() { // return the things StringBuilder logInfo = new StringBuilder("Canceled trade "); if (partner != null) { @@ -130,7 +130,7 @@ public int getMeso() { return meso; } - public void setMeso(int meso) { + public synchronized void setMeso(int meso) { if (locked) { throw new RuntimeException("Trade is locked."); } @@ -150,7 +150,7 @@ public void setMeso(int meso) { } } - public void addItem(IItem item) { + public synchronized void addItem(IItem item) { items.add(item); chr.getClient().getSession().write(MaplePacketCreator.getTradeItemAdd((byte) 0, item)); if (partner != null) { @@ -247,12 +247,26 @@ public static void completeTrade(MapleCharacter c) { } public static void cancelTrade(MapleCharacter c) { - c.getTrade().cancel(); - if (c.getTrade().getPartner() != null) { - c.getTrade().getPartner().cancel(); - c.getTrade().getPartner().getChr().setTrade(null); + MapleTrade local = c.getTrade(); + if (local == null) return; + MapleTrade partner = local.getPartner(); + if (partner == null) { + synchronized (local) { + local.cancel(); + c.setTrade(null); + } + } else { + MapleTrade first = local.getChr().getId() < partner.getChr().getId() ? local : partner; + MapleTrade second = local.getChr().getId() < partner.getChr().getId() ? partner : local; + synchronized (first) { + synchronized (second) { + local.cancel(); + partner.cancel(); + c.setTrade(null); + partner.getChr().setTrade(null); + } + } } - c.setTrade(null); } public static void startTrade(MapleCharacter c) {