Skip to content

Latest commit

 

History

History
555 lines (464 loc) · 21.8 KB

File metadata and controls

555 lines (464 loc) · 21.8 KB

KeplerC: Full Feature Parity with Java Kepler - Implementation Plan

Context

KeplerC currently has ~95 registered packet handlers. Java Kepler has ~185+. This plan ports all ~90 missing handlers to achieve full feature parity. Each handler will be verified against the Java source for exact protocol compatibility. New database tables, manager structs, and query files are needed for several feature categories.

Key Decisions

  • MESSAGES array: Already 9999, no change needed
  • Games: Full BattleBall + SnowStorm game logic (not just stubs)
  • Packet 159 conflict: Context-based routing (GETSELECTEDBADGES in normal rooms, GETINSTANCELIST in game lobbies)
  • Java verification: Check each Java handler source before writing C version

Conventions (from existing codebase)

  • Handlers are .h files in src/communication/incoming/{category}/ containing a single function
  • Registered in src/communication/message_handler.c via message_requests[ID] = HANDLER_NAME;
  • DB queries go in src/database/queries/{category}_query.h
  • Game structs/managers go in src/game/{feature}/
  • All handlers take (entity *player, incoming_message *message) signature
  • SQLite3 with thread-safe shared connection (global.DB)
  • Outgoing messages use om_create(header) + om_write_*() + player_send() + om_cleanup()

Phase 1: Simple Room User Actions (6 handlers)

Low complexity, no new DB tables needed. High user-visible impact.

Handlers to add:

ID Name File Description
56 WHISPER room/user/WHISPER.h Send whisper to specific user (like CHAT but only sent to sender+recipient)
115 GOAWAY room/user/GOAWAY.h Leave room (calls room_leave)
87 CARRYITEM room/user/CARRYITEM.h Carry hand item (like CARRYDRINK but for general items)
89 USEITEM room/items/USEITEM.h Use carried item (transfers carry->use status)
229 SET_SOUND_SETTING room/user/SET_SOUND_SETTING.h Toggle sound on/off (no-op server side, just ACK)
117 IIM room/user/IIM.h In-item interaction for gamehall games (stub initially)

Implementation details:

  • WHISPER: Read string "username message", split on first space, find target room_user by name, send outgoing header 25 (CHAT) with whisper flag to sender+recipient only. Check ignore list.
  • GOAWAY: Call room_leave(room, player) - essentially same as QUIT but explicit
  • CARRYITEM: Read string (item ID), set room_user status "carryd {id}", broadcast to room
  • USEITEM: Transfer "carryd" status to "usei", broadcast
  • SET_SOUND_SETTING: Read int (0/1), store on entity, no broadcast needed
  • IIM: Read string, parse gameId + command - stub with no-op for now

Files to modify:

  • src/communication/message_handler.c (add includes + registrations)
  • Create 6 new .h handler files

Phase 2: Room Moderation + Ignore System (6 handlers, 1 new DB table)

Essential for user experience - kick, rights management, ignore list.

New DB table:

CREATE TABLE IF NOT EXISTS users_mutes (
    user_id INTEGER NOT NULL,
    muted_id INTEGER NOT NULL,
    PRIMARY KEY (user_id, muted_id)
);

New files:

  • src/database/queries/player/ignore_query.h - CRUD for users_mutes
  • src/game/player/player.h - Add List *ignored_list to entity struct

Handlers:

ID Name File Description
95 KICK room/moderation/KICK.h Kick user from room (requires rights or owner)
155 REMOVEALLRIGHTS room/moderation/REMOVEALLRIGHTS.h Remove all rights from room
360 GET_IGNORE_LIST user/GET_IGNORE_LIST.h Load ignore list on login
319 IGNORE_USER user/IGNORE_USER.h Add user to ignore list
322 UNIGNORE_USER user/UNIGNORE_USER.h Remove from ignore list
263 GET_USER_TAGS room/user/GET_USER_TAGS.h Get user profile tags (stub)

Implementation details:

  • KICK: Read string (username), find user in room, verify kicker has rights/ownership/rank, call room_leave on target
  • REMOVEALLRIGHTS: Clear room rights list, delete all from rooms_rights table, broadcast update
  • Ignore list: Load on MESSENGERINIT, cache in entity->ignored_list. WHISPER/CHAT check list before delivering. DB table users_mutes(user_id, muted_id)
  • GET_USER_TAGS: Stub - send empty response (header 350)

Files to modify:

  • src/communication/message_handler.c
  • src/game/player/player.h (add ignored_list field)
  • src/game/player/player.c (init/cleanup ignored_list)
  • kepler.sql (add users_mutes table)
  • src/communication/incoming/room/user/CHAT.h and SHOUT.h (add ignore check)

Phase 3: User Features + Badges + Navigator (6 handlers)

Small standalone handlers for user profile and navigation.

Handlers:

ID Name File Description
228 GET_ACCOUNT_PREFERENCES user/settings/GET_ACCOUNT_PREFERENCES.h Send account prefs (sound on/off, etc.)
9 GETAVAILABLESETS user/GETAVAILABLESETS.h Available clothing sets for user
159 GETSELECTEDBADGES user/badges/GETSELECTEDBADGES.h Get a user's selected/active badges
154 GETSPACENODEUSERS navigator/GETSPACENODEUSERS.h Get user count in specific room node
34 INVITE_FRIEND messenger/INVITE_FRIEND.h Invite friend to current room
210 SCR_GIFT_APPROVAL user/SCR_GIFT_APPROVAL.h Club gift approval (send gift items)

Implementation details:

  • GET_ACCOUNT_PREFERENCES: Send outgoing (228) with sound/video boolean flags
  • GETAVAILABLESETS: Send clothing set list string based on club status + gender
  • GETSELECTEDBADGES: Read int (userId), lookup user badges, send active badges
  • GETSPACENODEUSERS: Read int (roomId), count users in that room, send count
  • INVITE_FRIEND: Read int (count) + int[] (userIds) + string (message), send room invite to each friend
  • SCR_GIFT_APPROVAL: Stub/simple response

Phase 4: Item Interactions (9 handlers, 1 new DB table)

Dice, dimmer/moodlight, presents, wheel of fortune, item state changes.

New DB table:

CREATE TABLE IF NOT EXISTS items_moodlight_presets (
    item_id INTEGER NOT NULL PRIMARY KEY,
    current_preset INTEGER NOT NULL DEFAULT 1,
    preset_1 TEXT NOT NULL DEFAULT '2,#000000,255',
    preset_2 TEXT NOT NULL DEFAULT '2,#000000,255',
    preset_3 TEXT NOT NULL DEFAULT '2,#000000,255'
);

New files:

  • src/database/queries/items/moodlight_query.h - CRUD for moodlight presets

Handlers:

ID Name File Description
76 THROW_DICE room/items/THROW_DICE.h Roll dice item (set to -1, schedule result)
77 DICE_OFF room/items/DICE_OFF.h Turn off/close dice
247 SPIN_WHEEL_OF_FORTUNE room/items/SPIN_WHEEL_OF_FORTUNE.h Spin wheel (set to -1, schedule result)
78 PRESENTOPEN room/items/PRESENTOPEN.h Open gift/present item, reveal contents
214 SETITEMSTATE room/items/SETITEMSTATE.h Set wall item state (on/off/numeric)
341 MSG_ROOMDIMMER_GET_PRESETS room/dimmer/MSG_ROOMDIMMER_GET_PRESETS.h Get moodlight presets
342 MSG_ROOMDIMMER_SET_PRESET room/dimmer/MSG_ROOMDIMMER_SET_PRESET.h Set moodlight preset
343 MSG_ROOMDIMMER_CHANGE_STATE room/dimmer/MSG_ROOMDIMMER_CHANGE_STATE.h Toggle moodlight on/off
28 GETDOORFLAT room/teleporter/GETDOORFLAT.h Teleporter interaction

Implementation details:

  • THROW_DICE/DICE_OFF: Find dice item by ID, verify adjacent, set custom_data="-1" (spinning), use uv_timer_t to schedule random result after 2s. DICE_OFF sets to "0"
  • SPIN_WHEEL_OF_FORTUNE: Similar to dice but 4.25s delay, random 1-20 result
  • PRESENTOPEN: Parse present custom_data (delimited: saleCode|sender|?|extra|time), create reward item from sale_code, delete present, update inventory
  • SETITEMSTATE: Read int (itemId) + int (state), find wall item, update custom_data, broadcast, save to DB
  • Dimmer handlers: 3 presets per moodlight item stored in DB. GET loads/creates, SET validates+updates, CHANGE_STATE toggles on/off
  • GETDOORFLAT: Read item ID, find linked teleporter, walk player to teleporter, warp to linked room after delay

Key dependency: Need timer/scheduled task support for dice/wheel animations. Check if hh_dispatch_timer_t from room walk timer can be reused.


Phase 5: Purse/Economy (2 handlers, 1 new DB table)

Credit log and voucher redemption.

New DB table:

CREATE TABLE IF NOT EXISTS vouchers (
    code TEXT NOT NULL PRIMARY KEY,
    credits INTEGER NOT NULL DEFAULT 0,
    item_id INTEGER DEFAULT NULL,
    is_redeemed INTEGER NOT NULL DEFAULT 0
);

Handlers:

ID Name File Description
127 GETUSERCREDITLOG purse/GETUSERCREDITLOG.h Return current credit balance (simple response)
129 REDEEM_VOUCHER purse/REDEEM_VOUCHER.h Validate voucher code, award credits/items

New files:

  • src/database/queries/player/voucher_query.h
  • src/communication/incoming/purse/ directory

Phase 6: Room Events System (6 handlers, 1 new DB table, 1 new manager)

Full room event creation, editing, listing, and expiration.

New DB table:

CREATE TABLE IF NOT EXISTS rooms_events (
    room_id INTEGER NOT NULL,
    user_id INTEGER NOT NULL,
    category_id INTEGER NOT NULL,
    name TEXT NOT NULL,
    description TEXT NOT NULL,
    expire_time INTEGER NOT NULL
);

New files:

  • src/game/events/event.h - Event struct
  • src/game/events/events_manager.h - Singleton manager with List of events
  • src/database/queries/events/event_query.h - CRUD queries
  • 6 handler files in src/communication/incoming/events/

Handlers:

ID Name File Description
345 CAN_CREATE_ROOMEVENT events/CAN_CREATE_ROOMEVENT.h Check if user can create event in current room
346 CREATE_ROOMEVENT events/CREATE_ROOMEVENT.h Create event: read category(int), name(str), desc(str)
348 EDIT_ROOMEVENT events/EDIT_ROOMEVENT.h Edit existing event
347 QUIT_ROOMEVENT events/QUIT_ROOMEVENT.h End/leave event
349 GET_ROOMEVENT_TYPE_COUNT events/GET_ROOMEVENT_TYPE_COUNT.h Get count per category
350 GET_ROOMEVENTS_BY_TYPE events/GET_ROOMEVENTS_BY_TYPE.h List events filtered by category

Manager: events_manager added to server struct in shared.h

  • Init: load non-expired events from DB, clean expired
  • Functions: create_event, remove_event, get_events_by_category, can_create_event
  • Event lifetime configurable (default 120 minutes)

Phase 7: Call For Help / Moderation System (7 handlers, 1 new manager)

In-memory CFH ticket system for moderators.

New files:

  • src/game/moderation/cfh/cfh.h - CallForHelp struct (callId, callerId, roomId, message, timestamp, pickerId, isOpen)
  • src/game/moderation/cfh/cfh_manager.h - Manager with hashtable of active calls
  • src/database/queries/moderation/moderation_action_query.h - Audit log
  • 7 handler files in src/communication/incoming/moderation/

New DB table (optional audit log):

CREATE TABLE IF NOT EXISTS moderation_audit_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    moderator_id INTEGER NOT NULL,
    target_id INTEGER,
    action_type INTEGER NOT NULL,
    message TEXT,
    timestamp INTEGER NOT NULL
);

Handlers:

ID Name File Description
200 MODERATORACTION moderation/MODERATORACTION.h Execute mod action (alert/kick/ban/mute)
237 REQUEST_CFH moderation/REQUEST_CFH.h Request pending CFH list (for mods)
86 SUBMIT_CFH moderation/SUBMIT_CFH.h Submit help request
48 PICK_CALLFORHELP moderation/PICK_CALLFORHELP.h Mod picks up CFH ticket
199 MESSAGETOCALLER moderation/MESSAGETOCALLER.h Mod replies to caller
198 CHANGECALLCATEGORY moderation/CHANGECALLCATEGORY.h Change CFH category
238 DELETE_CRY moderation/DELETE_CRY.h Delete/close CFH ticket

Manager: cfh_manager added to server struct

  • In-memory ConcurrentHashMap equivalent (hashtable with mutex)
  • Auto-incrementing call IDs
  • send_to_moderators() helper: iterate players, check fuseright rank, send packet
  • CFH struct: id, caller_id, room_id, message, timestamp, picker_id, is_open

Phase 8: Music/Trax System (12 handlers, 3 new DB tables, 1 new manager)

Full sound machine song creation, editing, and playlist management.

New DB tables:

CREATE TABLE IF NOT EXISTS soundmachine_songs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    soundmachine_id INTEGER NOT NULL,
    title TEXT NOT NULL,
    length INTEGER NOT NULL DEFAULT 0,
    data TEXT NOT NULL,
    burnt INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS soundmachine_tracks (
    soundmachine_id INTEGER NOT NULL,
    track_id INTEGER NOT NULL,
    slot_id INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS soundmachine_playlists (
    item_id INTEGER NOT NULL,
    song_id INTEGER NOT NULL,
    slot_id INTEGER NOT NULL
);

New files:

  • src/game/songs/song.h - Song struct
  • src/game/songs/song_manager.h - Manager
  • src/database/queries/songs/song_query.h
  • 12 handler files in src/communication/incoming/room/trax/

Handlers:

ID Name File
244/246 GET_SONG_LIST Already exists (extend for 246)
239 NEW_SONG room/trax/NEW_SONG.h
219 INSERT_SOUND_PACKAGE room/trax/INSERT_SOUND_PACKAGE.h
220 EJECT_SOUND_PACKAGE room/trax/EJECT_SOUND_PACKAGE.h
240 SAVE_SONG_NEW room/trax/SAVE_SONG_NEW.h
243 UPDATE_PLAY_LIST room/trax/UPDATE_PLAY_LIST.h
221 GET_SONG_INFO room/trax/GET_SONG_INFO.h
245 GET_PLAY_LIST room/trax/GET_PLAY_LIST.h
248 DELETE_SONG room/trax/DELETE_SONG.h
241 EDIT_SONG room/trax/EDIT_SONG.h
242 SAVE_SONG_EDIT room/trax/SAVE_SONG_EDIT.h
218 SAVE_SONG room/trax/SAVE_SONG.h
254 BURN_SONG room/trax/BURN_SONG.h

Phase 9: Jukebox (6 handlers, 1 new DB table)

Jukebox disk management.

New DB table:

CREATE TABLE IF NOT EXISTS soundmachine_disks (
    item_id INTEGER NOT NULL,
    soundmachine_id INTEGER NOT NULL,
    slot_id INTEGER NOT NULL,
    song_id INTEGER NOT NULL,
    burned_at INTEGER NOT NULL DEFAULT 0
);

New files:

  • src/game/jukebox/jukebox_manager.h
  • src/database/queries/songs/jukebox_query.h
  • 6 handler files in src/communication/incoming/room/jukebox/

Handlers:

ID Name File
258 GET_JUKEBOX_DISCS room/jukebox/GET_JUKEBOX_DISCS.h
259 GET_USER_SONG_DISCS room/jukebox/GET_USER_SONG_DISCS.h
255 ADD_JUKEBOX_DISC room/jukebox/ADD_JUKEBOX_DISC.h
256 REMOVE_JUKEBOX_DISC room/jukebox/REMOVE_JUKEBOX_DISC.h
257 JUKEBOX_PLAYLIST_ADD room/jukebox/JUKEBOX_PLAYLIST_ADD.h
260 RESET_JUKEBOX room/jukebox/RESET_JUKEBOX.h

Phase 10: Recycler/Ecotron (4 handlers, 2 new DB tables, 1 new manager)

New DB tables:

CREATE TABLE IF NOT EXISTS recycler_rewards (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    sale_code TEXT NOT NULL,
    item_cost INTEGER NOT NULL DEFAULT 5,
    recycling_session_time_seconds INTEGER NOT NULL DEFAULT 300,
    collection_time_seconds INTEGER NOT NULL DEFAULT 3660
);

CREATE TABLE IF NOT EXISTS recycler_sessions (
    user_id INTEGER NOT NULL PRIMARY KEY,
    reward_id INTEGER NOT NULL,
    items TEXT NOT NULL,
    session_started INTEGER NOT NULL DEFAULT 0
);

Handlers:

ID Name File
222 GET_FURNI_RECYCLER_CONFIGURATION recycler/GET_FURNI_RECYCLER_CONFIGURATION.h
223 GET_FURNI_RECYCLER_STATUS recycler/GET_FURNI_RECYCLER_STATUS.h
225 START_FURNI_RECYCLING recycler/START_FURNI_RECYCLING.h
226 CONFIRM_FURNI_RECYCLING recycler/CONFIRM_FURNI_RECYCLING.h

Phase 11: Infobus + Wobble Squabble + Pets (5 handlers)

Smaller systems.

Handlers:

ID Name File Description
111 CHANGEWORLD infobus/CHANGEWORLD.h Change world/connection (stub)
112 VOTE infobus/VOTE.h Vote on infobus question
113 TRYBUS infobus/TRYBUS.h Enter infobus room
114 PTM wobblesquabble/PTM.h Wobble squabble game move
128 GETPETSTAT pets/GETPETSTAT.h Get pet statistics

For pets, need:

CREATE TABLE IF NOT EXISTS items_pets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    type INTEGER NOT NULL,
    race INTEGER NOT NULL,
    colour TEXT NOT NULL,
    nature_positive INTEGER NOT NULL DEFAULT 0,
    nature_negative INTEGER NOT NULL DEFAULT 0,
    last_kip INTEGER NOT NULL DEFAULT 0,
    last_eat INTEGER NOT NULL DEFAULT 0,
    last_drink INTEGER NOT NULL DEFAULT 0,
    last_playtoy INTEGER NOT NULL DEFAULT 0,
    last_playuser INTEGER NOT NULL DEFAULT 0,
    born INTEGER NOT NULL DEFAULT 0
);

Phase 12: Tutorial/Welcoming Party (9 handlers)

Tutorial/guide system. Mostly stubs since the client flow is self-contained.

Handlers:

ID Name File
357 ACCEPT_TUTOR_INVITATION welcomingparty/ACCEPT_TUTOR_INVITATION.h
358 REJECT_TUTOR_INVITATION welcomingparty/REJECT_TUTOR_INVITATION.h
356 MSG_INVITE_TUTORS tutorial/MSG_INVITE_TUTORS.h
355 MSG_GET_TUTORS_AVAILABLE tutorial/MSG_GET_TUTORS_AVAILABLE.h
362 MSG_WAIT_FOR_TUTOR_INVITATIONS tutorial/MSG_WAIT_FOR_TUTOR_INVITATIONS.h
363 MSG_CANCEL_WAIT_FOR_TUTOR_INVITATIONS tutorial/MSG_CANCEL_WAIT_FOR_TUTOR_INVITATIONS.h
313 MSG_REMOVE_ACCOUNT_HELP_TEXT tutorial/MSG_REMOVE_ACCOUNT_HELP_TEXT.h
359 MSG_CANCEL_TUTOR_INVITATIONS tutorial/MSG_CANCEL_TUTOR_INVITATIONS.h
249 RESET_TUTORIAL tutorial/RESET_TUTORIAL.h

Phase 13: Games/Minigames (13 handlers, new game framework)

Most complex phase. BattleBall + SnowStorm game framework.

New DB tables:

CREATE TABLE IF NOT EXISTS games_ranks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    game_type TEXT NOT NULL,
    rank_name TEXT NOT NULL,
    min_points INTEGER NOT NULL DEFAULT 0,
    max_points INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS games_maps (
    model TEXT NOT NULL PRIMARY KEY,
    game_type TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS games_spawns (
    game_type TEXT NOT NULL,
    map_id INTEGER NOT NULL,
    team_id INTEGER NOT NULL,
    x INTEGER NOT NULL,
    y INTEGER NOT NULL,
    z REAL NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS games_player_stats (
    user_id INTEGER NOT NULL,
    game_type TEXT NOT NULL,
    points_all_time INTEGER NOT NULL DEFAULT 0,
    PRIMARY KEY (user_id, game_type)
);

New files:

  • src/game/games/game.h - Base game struct
  • src/game/games/game_manager.h - Game instance manager
  • src/game/games/game_player.h - Per-player game state
  • src/game/games/battleball/ - BattleBall logic
  • src/game/games/snowstorm/ - SnowStorm logic
  • src/database/queries/games/game_query.h
  • 13 handler files in src/communication/incoming/games/

Handlers:

ID Name
159 GETINSTANCELIST (conflicts with GETSELECTEDBADGES - context-based routing)
160 OBSERVEINSTANCE
161 UNOBSERVEINSTANCE
162 INITIATECREATEGAME
163 GAMEPARAMETERVALUES
165 INITIATEJOINGAME
167 LEAVEGAME
168 KICKPLAYER
169 WATCHGAME
170 STARTGAME
171 GAMEEVENT
172 GAMERESTART
173 REQUESTFULLGAMESTATUS

Packet 159 conflict resolution: Context-based routing in message_handler_invoke(). If player is in a game lobby room (detected via room model prefix "bb_" or "ss_"), route to GETINSTANCELIST. Otherwise route to GETSELECTEDBADGES. This requires a wrapper function registered at 159 that dispatches based on context.

Full game logic: This phase implements complete BattleBall and SnowStorm including tile maps, teams, power-ups, scoring, game loops, and win conditions. This is the largest single phase and will reference the Java Game, BattleBallGame, SnowStormGame, GamePlayer, GameTile, PowerUp classes extensively.


Phase 14: Groups (2 handlers, 2 new DB tables, 1 new manager)

New DB tables:

CREATE TABLE IF NOT EXISTS groups_details (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    description TEXT,
    owner_id INTEGER NOT NULL,
    room_id INTEGER NOT NULL,
    badge TEXT NOT NULL DEFAULT '',
    created_at INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS groups_memberships (
    user_id INTEGER NOT NULL,
    group_id INTEGER NOT NULL,
    is_pending INTEGER NOT NULL DEFAULT 0,
    member_rank INTEGER NOT NULL DEFAULT 0,
    PRIMARY KEY (user_id, group_id)
);

Handlers:

ID Name Description
230 GET_GROUP_BADGES Get group badges for room users
231 GET_GROUP_INFO Get group details by ID

Structural Changes Required

shared.h - Add to server struct:

struct events_manager events_manager;
struct cfh_manager cfh_manager;
struct song_manager song_manager;
struct recycler_manager recycler_manager;
struct game_manager game_manager;
struct group_manager group_manager;

player.h - Add to entity struct:

List *ignored_list;        // cached user IDs
int favourite_group_id;    // display group
bool sound_enabled;        // sound setting

message_handler.h - MESSAGES already 9999, no change needed

kepler.sql - Add all new CREATE TABLE statements


Build & Test Verification

After each phase:

  1. Run cmake --build build via msys2 bash
  2. Fix any compilation errors
  3. Test with Habbo client connecting and triggering the new packet handlers
  4. Verify no crashes via debug logging (configuration_get_bool("debug"))

Implementation Order

Phases 1-5 first (core gameplay), then 6-7 (events+moderation), then 8-9 (music), then 10-14 (remaining systems). Each phase is independently testable.