L2J

Install and integrate L2J

Build one type-safe ForgeportIntegration class for your Java game server

This page is for a source-based Java game server. The goal is one integration class that describes only what Forgeport cannot know about your pack: database access, password verification, character queries, live player lookups, and the pack calls that make game changes.

Install the connector

Download these files from Admin → Game Servers:

portal-connector-core-2.0.0.jar
minimal-json-0.9.5.jar
portal.properties
ForgeportIntegration.java

Add both JARs to the build and runtime classpath. The connector targets Java 17.

Write the smallest usable integration

Place this class in your game-server source and replace each placeholder with the equivalent API or SQL from your pack.

ForgeportIntegration.java
package com.yourpack.gameserver.portal;

import net.forgeport.connector.Bridge;
import net.forgeport.connector.Delivery;
import net.forgeport.connector.Forgeport;
import net.forgeport.connector.model.CredentialsResult;
import net.forgeport.connector.model.GameCharacter;
import net.forgeport.connector.model.GameItem;

public final class ForgeportIntegration
{
    public static void start()
    {
        Forgeport.start(Bridge.builder()
            .database(YourConnectionPool::getConnection)
            .credentials((account, password) -> {
                // Query the account and verify the pack's real password format.
                return CredentialsResult.INVALID;
            })
            .characters(account -> {
                // Query this account and return List<GameCharacter>.
                return java.util.List.of();
            })
            .items(() -> YourItemTable.templates().stream()
                .map(item -> new GameItem(item.id(), item.name())))
            .itemDelivery((target, item) -> {
                YourPlayer player = YourWorld.player(target.requiredCharacterId());
                if (player == null)
                    return Delivery.offline();
                if (!player.account().equalsIgnoreCase(target.account()))
                    return Delivery.invalidTarget();
                if (YourItemTable.template(item.itemId()) == null)
                    return Delivery.invalidItem();

                return Delivery.apply(() -> player.addItem(
                    item.itemId(), item.count(), item.enchantLevel()));
            })
            .playerOnlineEvents()
            .build());
    }
}

The five registrations above power account linking, character selection, item search, item delivery, and delivery resumption after login. Registering the code automatically advertises its protocol capability; there is no capability string list to maintain.

Supply database access

.database(...) accepts a connection provider. Forgeport uses it only for its own portal_command delivery journal. It never queries or changes your account, character, inventory, or world tables.

The database user needs:

  • CREATE, SELECT, INSERT, and UPDATE on portal_command;
  • ALTER only when upgrading a journal created by an older connector core;
  • whatever separate rights your pack-specific providers need for their SQL.

Map pack data to domain objects

Pack code returns Java values, not protocol JSON. For example, a character row becomes:

ForgeportIntegration.java
new GameCharacter(
    id, name, level, classId, race, sex,
    online, pvpKills, pkKills, clanName)

The same boundary applies to GameItem, GameSkill, CharacterSkill, GameClan, GameSiege, and GameBoss. Forgeport validates and serializes the result.

Return prepared deliveries

Run every safe refusal check first. If the change can proceed, return a prepared operation:

  • Delivery.apply(() -> packMutation) for a live-world change;
  • Delivery.applyInTransaction(connection -> sqlMutation) for a durable, database-only entitlement;
  • Delivery.offline(), retryable(...), refused(...), or a typed helper when the change should not run.

Forgeport validates the result, records APPLYING, runs the prepared change, records APPLIED, and only then responds. See Give purchased items to characters for the practical flow.

Wire lifecycle hooks

Start after the world and data tables are initialized:

GameServer.java
ForgeportIntegration.start();

After the player is fully present in the world:

EnterWorld.java
Forgeport.playerOnline(player.getAccountName(), player.getObjectId());

During shutdown:

Shutdown.java
Forgeport.shutdown();

Verify

  1. Build and restart the GameServer.
  2. Confirm Admin → Game Servers shows Connected.
  3. Run Connector check.
  4. Link a real test account and confirm its characters appear.
  5. Deliver one test item online, then one while offline and log in.

For stock code placement and pack calls, use the complete aCis 409 or Mobius High Five integration.

On this page