L2J

Give purchased items to characters

Expose the item catalog and apply item purchases through pack APIs

Item support has two parts. The catalog lets an admin find "Adena" or a Blessed Scroll while creating content. Delivery turns a completed purchase into the pack's normal addItem(...) call. Both are required.

Expose the item catalog

Register a fresh stream of the item templates already loaded by the game server. Put this inside the Bridge.builder() call in ForgeportIntegration.java; add the java.util.Arrays and java.util.Objects imports if the file does not already have them:

ForgeportIntegration.java
.items(() -> Arrays.stream(ItemData.getInstance().getTemplates())
    .filter(Objects::nonNull)
    .map(item -> new GameItem(item.getItemId(), item.getName())))

Your code maps pack objects to GameItem. Forgeport normalizes the admin's query, ranks exact/prefix/substring matches, sorts the result, removes duplicate ids, applies the limit, and serializes the response.

Deliver an item

If a player buys 10 Blessed Scrolls of Escape, the flow is:

Purchase completes
  → Forgeport calls .itemDelivery(...) with the selected character and item
  → your code validates the live player, account, and item template
  → your code returns a prepared Delivery
  → Forgeport records APPLYING
  → the pack's addItem(...) runs
  → Forgeport records APPLIED and reports the result

The handler prepares the game change; it must not call addItem(...) before returning Delivery.apply(...).

ForgeportIntegration.java
.itemDelivery((target, item) -> {
    Player player = World.getInstance()
        .getPlayer(target.requiredCharacterId());

    if (player == null)
        return Delivery.offline();
    if (!player.getAccountName().equalsIgnoreCase(target.account()))
        return Delivery.invalidTarget();
    if (ItemData.getInstance().getTemplate(item.itemId()) == null)
        return Delivery.invalidItem();

    return Delivery.apply(() -> grantItems(player, item));
})

grantItems(...) is your pack-specific helper that calls the normal item API. Preserve enchant level and create non-stackable quantities one unit at a time; the complete examples show the exact handling required by their revisions.

Never mutate before returning Delivery.apply

Forgeport must write the delivery journal before the actual game change. An early mutation can be repeated after a reconnect and bypasses the safety guarantees of the connector core.

Offline characters

The reference handlers return Delivery.offline(). Forgeport holds the delivery, and the explicit EnterWorld hook resumes it:

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

Call this only after the player is fully present in the live world.

Verify

  1. In Admin, search the item picker for Adena and confirm item 57 appears.
  2. Run Connector check with a designated test character.
  3. Buy one low-value item with a test account.
  4. Confirm it appears once on the selected character.
  5. Repeat once with the character offline, then log in and confirm delivery resumes.

For the exact journal state machine, see L2J architecture.

On this page