Connect your L2Off extender
Choose and implement the game-side path that consumes delivery jobs
The connector can queue a purchase, but only code inside the game server can apply it to a live character. Decide which situation matches the extender you actually deploy before leaving item delivery enabled.
| Situation | What you have | Supported delivery |
|---|---|---|
| Buildable extender | Source and a working toolchain | Items and any other jobs you implement |
| Existing item-delivery extender | Built Vanganth, ilExt, or compatible fork | Items only |
| No delivery feature | Built extender with no queue consumer | None; the server cannot serve purchases |
If you can build the extender
Create the shared queue and stored procedures:
l2off/portal_delivery/schema.sql on GitHubThe contract is:
L2Serverpolls after EnterWorld, applies each job to the liveUser, and acknowledges it.CacheDrunslin_PortalDeliveryAskandlin_PortalDeliveryDoneover the existing socket.- Items use the extender's inventory call; skills use its acquisition and DB persistence calls.
- Both DLLs must be deployed and restarted together.
Enable the poll in the extender configuration:
[ItemDelivery]
Enabled = 1
CheckInterval = 60Preserve the positional SQL contract
The CacheD side binds columns by position and without indicator variables. The
stored procedure must return every column in the documented order, using
ISNULL for fields that do not apply to the current job. amount is INT
because the extender ODBC helper binds 32-bit integers.
Require QUOTED_IDENTIFIER ON
The filtered unique index on command_id and every write through it require
QUOTED_IDENTIFIER ON. Create both procedures with the setting enabled so SQL
Server stores it with the module; CacheD's ODBC session otherwise runs with it
off.
SELECT o.name, m.uses_quoted_identifier
FROM sys.sql_modules m
JOIN sys.objects o ON o.object_id = m.object_id
WHERE o.name LIKE 'lin_PortalDelivery%';Both rows must read 1.
A wrong setting can duplicate paid items
If the acknowledgement update fails, the row remains pending and the extender can give the same item again on every poll. Do not enable sales until the query above and a replay test both pass.
Keep packet handlers inside the real table bounds
Handler tables are fixed-size arrays followed by per-opcode flags. Binding past the actual initialized size corrupts the next array instead of failing cleanly. Read the bound from the initializer, prefer an opcode still connected to the do-nothing stub, and use one opcode plus append-only subcommands for multiple Forgeport jobs.
If the extender already delivers items
Use its required table and positional columns, enable its ItemDelivery
feature, and point [delivery.item].sql at that queue. Common built extenders
use:
jobId, charId, itemId, itemAmount, enchantRemove [delivery.skill], rename, gender, and unstuck sections unless that exact
build consumes them. A working item-only portal is safer than advertised jobs
that never run.
If there is no delivery path
Remove all item and skill delivery sections. The connector will not advertise them, and the server remains unavailable to players because purchased items cannot be delivered. The safe options are obtaining buildable extender source or deploying an extender with a real delivery feature.
Verify
- Compare the deployed DLL hashes with the build output.
- Restart L2Server and CacheD together.
- Queue one item and observe ask, apply, and acknowledgement.
- Replay the same command id and confirm no second item appears.
- Test a refused job and confirm the player's order closes with coins returned.