{"id":44843719,"url":"https://github.com/ruusey/jrealm","last_synced_at":"2026-02-17T04:03:42.115Z","repository":{"id":219463099,"uuid":"687049875","full_name":"ruusey/jrealm","owner":"ruusey","description":"An 8bit Dungeon Crawler Rogue-Like MMO written entirely in Java","archived":false,"fork":false,"pushed_at":"2026-02-03T02:49:36.000Z","size":6391,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-03T16:41:55.050Z","etag":null,"topics":["game","game-development","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruusey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-09-04T13:50:45.000Z","updated_at":"2026-02-03T02:49:41.000Z","dependencies_parsed_at":"2024-02-03T21:34:09.767Z","dependency_job_id":"ed1c6446-8718-4b39-a460-b72f5e17a670","html_url":"https://github.com/ruusey/jrealm","commit_stats":null,"previous_names":["ruusey/jrealm"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/ruusey/jrealm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruusey%2Fjrealm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruusey%2Fjrealm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruusey%2Fjrealm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruusey%2Fjrealm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruusey","download_url":"https://codeload.github.com/ruusey/jrealm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruusey%2Fjrealm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29532984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T03:01:11.216Z","status":"ssl_error","status_checked_at":"2026-02-17T03:00:31.803Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["game","game-development","java"],"created_at":"2026-02-17T04:03:41.371Z","updated_at":"2026-02-17T04:03:42.110Z","avatar_url":"https://github.com/ruusey.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JRealm\n### An 8bit Dungeon Crawler Rogue-Like Game written entirely in Java\n\n\u003cdiv dsplay=\"inline\"\u003e\n\u003cimg src=\"https://i.imgur.com/WTsZ0Td.png\" width=\"312\"\u003e\n\u003cimg src=\"https://i.imgur.com/QdzDjJh.png\" width=\"312\"\u003e\n\u003cimg src=\"https://i.imgur.com/PXBqnlh.png\" width=\"312\"\u003e\n\u003cimg src=\"https://i.imgur.com/glqEiZJ.png\" width=\"312\"\u003e\n\u003cimg src=\"https://i.imgur.com/G9ilNvu.png\" width=\"312\"\u003e\n\u003cimg src=\"https://i.imgur.com/8Yz96Oe.png\" width=\"312\"\u003e\n\u003cimg src=\"https://i.imgur.com/5NYzaBo.png\" width=\"612\"\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\n### Game Controls\n* **W** - Up \u003cbr/\u003e\n* **A** - Left \u003cbr/\u003e\n* **S** - Down \u003cbr/\u003e\n* **D** - Right \u003cbr/\u003e\n\n* **1-8** - Consume/Equip corresponding inventory slot \u003cbr/\u003e\n\n* **Left Click** - Shoot/Pick up loot \u003cbr/\u003e\n* **Right Click** - Use Ability/Drop item \u003cbr/\u003e\n\n* **F1** - Teleport to vault (safe zone) \u003cbr/\u003e\n* **F2** - Use nearest portal \u003cbr/\u003e\n\n* **Enter** - Chat/Use command\n### Running\n#### *NOTE: As of release 0.3.0 You are now required to run JRealm-Data alongside JRealm-Server see: https://github.com/ruusey/jrealm-data*\n\n* **General**: \u003cbr/\u003e\n```java -jar ./jrealm-{version}.jar {-client | -server | -embedded} {DATA_SERVER_ADDR}```\n\n* **Server**: \u003cbr /\u003e\n```java -jar ./jrealm-{version}.jar -server {DATA_SERVER_ADDR}``` \u003cbr /\u003e\nThe server requires port 2222 to be available\n\n* **Client**: \u003cbr /\u003e\n```java -jar ./jrealm-{version}.jar -client {DATA_SERVER_ADDR}``` \u003cbr /\u003e\nReplace `{SERVER_ADDRESS}` with the IP Address of the server you wish to connect to \u003cbr /\u003e\nReplace `{PLAYER_EMAIL}` with your account's email \u003cbr /\u003e\nReplace `{PLAYER_PASSWORD}` with your account's password \u003cbr /\u003e\nReplace `{CHARACTER_UUID}` with the UUID of your character \n\n### Game Data\nSee [JRealm-Data](https://github.com/ruusey/jrealm-data) for information on modifying the game's .json data\n\n## Packet Handlers\nApplicable classes: \n`com.jrealm.game.realm.RealmManagerServer, com.jrealm.game.realm.RealmManagerClient, com.jrealm.net.server.ServerGameLogic, com.jrealm.net.server.ClientGameLogic`\n\n**JRealm** packet handlers exist on both the server and client realm managers to hook callbacks into recieved packets. In general packet callbacks are registered during the `registerPacketCallbacks()`\nroutine of `RealmManagerClient` and `RealmManagerServer`. Packet callback methods will typically be a static method with signature `BiConsumer\u003cRealmManager, Packet\u003e` that is passed the target packet and Realm Manager on receiving the packet, although\nany method matching this signature can be used as a packet callback. \n\u003cbr /\u003e\n\n**As of JRealm 0.3.5**, developers can now make use of the **@PacketHandler(Class\u003c? extends Packet\u003e packetClass)** annotation to mark a method as a packet handler in server code. Generally\nThe **@PacketHandler** annotation sacrifices performance for convenience as the reflection mechanism used to trigger such callbacks is computationally expensive.\n\n**Example (Register by mapping, high performance)**\n```java\n// RealmManagerServer.java\nprivate void registerPacketCallbacks() {\n    this.registerPacketCallback(PacketType.PLAYER_MOVE.getPacketId(), ServerGameLogic::handlePlayerMoveServer);\n}\n\n// ServerGameLogic.java\npublic static void handlePlayerMoveServer(RealmManagerServer mgr, Packet packet) {\n    final PlayerMovePacket playerMovePacket = (PlayerMovePacket) packet;\n    final Realm realm = mgr.searchRealmsForPlayer(playerMovePacket.getEntityId());\n    if (realm == null) {\n        ServerGameLogic.log.error(\"Failed to get realm for player {}\", playerMovePacket.getEntityId());\n        return;\n    }\n    final Player toMove = realm.getPlayer(playerMovePacket.getEntityId());\n    if (toMove.hasEffect(EffectType.PARALYZED))\n        return;\n    boolean doMove = playerMovePacket.isMove();\n    float spd = (float) ((5.6 * (toMove.getComputedStats().getSpd() + 53.5)) / 75.0f);\n    spd = spd/1.5f;\n    if (playerMovePacket.getDirection().equals(Cardinality.NORTH)) {\n        toMove.setUp(doMove);\n        toMove.setDy(doMove ? -spd : 0.0f);\n    }\n}\n```\n**Example (Register by annotation,  ease of use, introduces some but minimal performance overhead)**\n```java\n@PacketHandler(TextPacket.class)\npublic static void handleText0(RealmManagerServer mgr, Packet packet) {\n    final TextPacket textPacket = (TextPacket) packet;\n    final long fromPlayerId = mgr.getRemoteAddresses().get(textPacket.getSrcIp());\n    if(!validateCallingPlayer(mgr, packet, fromPlayerId)) {\n        return;\n    }\n    final Player player = mgr.searchRealmsForPlayer(fromPlayerId);\n    final Realm realm = mgr.findPlayerRealm(fromPlayerId);\n\n    log.info(\"Player {} says {} from Realm {}\", player.getName(), textPacket.getMessage(), realm.getRealmId());\n}\n```\n## Command Handlers\nApplicable classes: \n`com.jrealm.game.messaging.*, com.jrealm.net.server.ServerGameLogic, com.jrealm.net.server.ServerCommandHandler`\n\n**JRealm** command handlers are a similar subset of functionality to the Packet Callbacks mentioned in the previous section\nthat allow users to embed  server commands in the packets they send to **JRealm-Server**. \u003cbr /\u003e\nCommandPackets consist of a `byte commandId` and a `UTF JSON String command`. When the server recieves a Command Packet it will attempt to deserialize the JSON payload into the model\ntargeted by this Command `(defined in com.jrealm.game.messaging.CommandType)`\n\nThe class `ServerCommandHandler` is responsible for handling individual Command functionality. Each command callback is registered dynamically\nat runtime\n\n**Example**:\n```java\n\n    @CommandHandler(\"setstats\")\n    private static void invokeSetStats(RealmManagerServer mgr, Player target, ServerCommandMessage message) {\n        if (message.getArgs() == null || message.getArgs().size() != 2)\n            throw new IllegalArgumentException(\"Usage: /setstat {STAT_NAME} {STAT_VALUE}\");\n        final short valueToSet = Short.parseShort(message.getArgs().get(1));\n        log.info(\"Player {} set stat {} to {}\", target.getName(), message.getArgs().get(0), valueToSet);\n        switch (message.getArgs().get(0)) {\n        case \"hp\":\n            target.getStats().setHp(valueToSet);\n            break;\n        case \"mp\":\n            target.getStats().setMp(valueToSet);\n            break;\n        ...\n    }\n```\nNote: Command Handler methods are allowed to throw `Exception` to control flow through the handler. By default if an Exception is thrown, its message\nwill be tranformed into a 502 error code message that is returned to the client.\n\n\n### Creating Maps \u0026 Terrains\nSee https://github.com/ruusey/jrealm-data?tab=readme-ov-file#adding-static-data-maps\n\n## Available Scripted Behaviors\n**JRealm** provides developers with an ever expanding arsenal of tools for implementing game content. The existing toolkit is \ncentered around providing script-like features for modifying the games state and data on the server side. \nThe current script features are:\n\n### Enemy Scripts\nEnemyScripts are small classes that currently allow a developer to implement the `attack` method to provide custom attack behavior. EnemyScripts\nwill replace the target enemies default attack pattern with the contents of your script. \u003cbr /\u003e\nEnemyScripts are run asynchronously and thus support delays and long running attack patterns. Any class extending `EnemyScriptBase` will be loaded as an EnemyScript at runtime.\n\n**Example**\n```java\npublic class Enemy10Script extends EnemyScriptBase {\n    // Default constructor\n    public Enemy10Script(RealmManagerServer mgr) {\n        super(mgr);\n    }\n    \n    // Target enemy ID\n    @Override\n    public int getTargetEnemyId() {\n        return 10;\n    }\n    \n    @Override\n    public void attack(Realm targetRealm, Enemy enemy, Player targetPlayer) throws Exception {\n    \n        Player target = targetPlayer;\n        Vector2f dest = target.getBounds().getPos().clone(target.getSize() / 2, target.getSize() / 2);\n    \n        Vector2f source = enemy.getPos().clone(target.getSize() / 2, target.getSize() / 2);\n        float angle = Bullet.getAngle(source, dest);\n        // Get the projectiles for attack ID 2\n        ProjectileGroup group = GameDataManager.PROJECTILE_GROUPS.get(2);\n        Projectile p = group.getProjectiles().get(0);\n        // Create two enemy projectiles with the given data with a 100ms delay in between\n        this.createProjectile(p, targetRealm.getRealmId(), target.getId(), source.clone(), angle, group);\n        this.sleep(100);\n        this.createProjectile(p, targetRealm.getRealmId(), target.getId(), source.clone(), angle, group);\n    }\n}\n```\n\n### UseableItem Scripts\nUseableItem scripts are small classes that currently allow developers to implement custom item on use and on consume behavior. Currently useable item scripts allow the \ndeveloper to implement the `invokeUseItem` and `invokeItemAbility` which will respectively override the behavior of an items on-equip action and its use while equipped in the\nplayers ability slot. Any class extending `UseableItemScriptBase` will be loaded as a UseableItem script at runtime.\n\n**Example**\n```java \n// Item script that adds the ability items effect\n// to surrounding players\npublic class Item156Script extends UseableItemScriptBase{\n    \n    public Item156Script(RealmManagerServer mgr) {\n        super(mgr);\n    }\n    \n    @Override\n    public void invokeUseItem(Realm targetRealm, Player player, GameItem item) {\n    }\n    \n    @Override\n    public void invokeItemAbility(Realm targetRealm, Player player, GameItem abilityItem) {\n        for (final Player other : targetRealm\n                .getPlayersInBounds(targetRealm.getTileManager().getRenderViewPort(player))) {\n            other.addEffect(abilityItem.getEffect().getEffectId(), abilityItem.getEffect().getDuration());\n        }\n    }\n    \n    @Override\n    public int getTargetItemId() {\n        return 156;\n    }\n}\n```\n\n### Terrain Decorator Scripts\nTerrainDecorator scripts are small classes that currently allow developers to implement custom terrain post processing for Realms during their generation process. TerrainDecorators currently\nallow the developer to implement the `decorate` method to modify world tiles, spawn enemies or generate structures. Any class extending `RealmDecoratorBase` will be loaded as a TerrainDecorator\nscript at runtime.\n\n**Example**\n```java\n// Creates slowing water pool decorations in the Beach Realm\npublic class Beach0Decorator extends RealmDecoratorBase {\n    private static final Integer MIN_WATER_POOL_COUNT = 15;\n    private static final Integer MAX_WATER_POOL_COUNT = 25;\n    private static final TileModel WATER_TILE = GameDataManager.TILES.get(41);\n    private static final TileModel WATER_TILE_DEEP = GameDataManager.TILES.get(42);\n    \n    public Beach0Decorator(RealmManagerServer mgr) {\n        super(mgr);\n    }\n    \n    @Override\n    public void decorate(final Realm input) {\n        for (int i = 0; i \u003c (Beach0Decorator.MIN_WATER_POOL_COUNT + Realm.RANDOM\n                .nextInt(Beach0Decorator.MAX_WATER_POOL_COUNT - Beach0Decorator.MIN_WATER_POOL_COUNT)); i++) {\n            final Vector2f pos = input.getTileManager().randomPos();\n            final TileMap baseLayer = input.getTileManager().getBaseLayer();\n            final int centerX = (int) (pos.x / baseLayer.getTileSize());\n            final int centerY = (int) (pos.y / baseLayer.getTileSize());\n    \n            baseLayer.setBlockAt(centerX, centerY, (short) Beach0Decorator.WATER_TILE_DEEP.getTileId(),\n                    Beach0Decorator.WATER_TILE_DEEP.getData());\n            baseLayer.setBlockAt(centerX, (centerY - 1) \u003e -1 ? centerY - 1 : 0,\n                    (short) Beach0Decorator.WATER_TILE.getTileId(),\n                    Beach0Decorator.WATER_TILE.getData());\n            baseLayer.setBlockAt(centerX,\n                    (centerY + 1) \u003e= baseLayer.getHeight() ? baseLayer.getHeight() - 1 : centerY + 1,\n                            (short) Beach0Decorator.WATER_TILE.getTileId(),\n                            Beach0Decorator.WATER_TILE.getData());\n            baseLayer.setBlockAt((centerX - 1) \u003e -1 ? centerX - 1  : 0, centerY, (short) Beach0Decorator.WATER_TILE.getTileId(),\n                    Beach0Decorator.WATER_TILE.getData());\n            baseLayer.setBlockAt((centerX + 1) \u003e= baseLayer.getWidth() ? baseLayer.getWidth()-1 : centerX + 1 , centerY, (short) Beach0Decorator.WATER_TILE.getTileId(),\n                    Beach0Decorator.WATER_TILE.getData());\n    \n        }\n    }\n    \n    @Override\n    public Integer getTargetMapId() {\n        return 2;\n    }\n}\n```\n\n\n# Important classes:\n\n* `com.jrealm.game.states.PlayState`\n* `com.jrealm.game.realm.Realm`\n* `com.jrealm.game.realm.tile.TileManager`\n* `com.jrealm.net.server.ServerCommandHandler`\n* `com.jrealm.net.server.ServerItemHandler`\n* `com.jrealm.net.server.ProcessingThread`\n* `com.jrealm.game.realm.RealmManagerServer`\n* `com.jrealm.game.realm.RealmManagerClient`\n* `com.jrealm.game.entity.Enemy`\n* `com.jrealm.game.entity.Portal`\n* `com.jrealm.game.entity.LootContainer`\n* `com.jrealm.game.entity.Player`\n* `com.jrealm.game.entity.Bullet`\n* `com.jrealm.net.client.SocketClient`\n* `com.jrealm.net.client.ClientGameLogic`\n* `com.jrealm.net.server.SocketServer`\n* `com.jrealm.net.server.ServerGameLogic`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruusey%2Fjrealm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruusey%2Fjrealm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruusey%2Fjrealm/lists"}