Skip to content

hopskipnfall/EmuLinker-K

Repository files navigation

Maintained build Crowdin

Watch on GitHub Star on GitHub Tweet

EmuLinker-K

EmuLinker-K (ELK) is a high-performance server that uses the Kaillera protocol to facilitate online multiplayer for retro gaming emulators.

Built as a Kotlin rewrite of the classic EmulinkerSF, EmuLinker-K focuses on maximizing performance and stability, patching legacy security and privacy vulnerabilities, and providing useful new features for both server administrators and players.

Please help us translate the server into more languages by contributing to our CrowdIn project

Feel free to file bugs and feature requests on this repository, or find our channel in the Kaillera Reborn discord:

Discord

Setting up a new server (or updating an existing one)

Linux/MacOS

To install or update the server, simply run the following command in your terminal. If you are updating an existing server, run the command from your EmuLinker-K folder:

curl -fsSL https://raw.githubusercontent.com/hopskipnfall/EmuLinker-K/master/release/setup.sh | bash

Windows

Please download the .zip file attached to latest release on the Releases page, extract it, and run start-server.bat.

Configuration files

config/emulinker.cfg

This is the main configuration file for the server.

The most important settings to configure are: emulinker.language and emulinker.charset.

Here is an exhaustive list of supported options to set in emulinker.cfg (most of these you will not need to change):

Config Key Default Value Description
controllers.connect.port 27888 The port used by the server for all communication. Most clients will default to 27888 as the default.
controllers.v086.bufferSize 4096 Buffer size for v086 protocol in bytes.
controllers.v086.clientTypes.clientType v086 Comma-separated list of accepted protocols.
emulinker.charset (required) The character set that matches your language region. Use Windows-1252 for English and Western Europe, Windows-1251 for Cyrillic, GBK for Simplified Chinese, Shift_JIS for Japanese, or x-IBM949 for Korean. Note: Unicode (UTF-8) is generally not supported by clients. If text appears garbled or displays as '?', try selecting a different charset.
emulinker.language custom Strict ISO 639-1 language code. Use en for English, es for Spanish, ja for Japanese, ko for Korean, pt for Portuguese, ru for Russian, or zh for Simplified Chinese, or custom if you wish to provide your own translations.
game.bufferSize (required) Buffer size for the game data queue. Increasing this will not improve performance. Should be equal to at least the largest single game packet.
game.defaultAutoFireSensitivity 0 Auto-fire sensitivity. 0 (disabled) through 5 (maximum sensitivity)
masterList.serverConnectAddress Server address for master list registration
masterList.serverLocation Unknown Server location for master list display
masterList.serverName Not Set Name used to identify the server
masterList.serverWebsite Server website URL for master list
masterList.touchEmulinker FALSE Registers server with the EmuLinker master list
masterList.touchKaillera FALSE Registers server with the Kaillera master list
metrics.enabled FALSE Enables detailed monitoring of server health
metrics.loggingFrequencySeconds 30 How frequently to log aggregated metrics for debugging
server.allowedConnectionTypes 1,2,3,4,5,6 Comma-separated list of allowed connection types by their ID (1 = LAN, 2 = Excellent, 3 = Good, 4 = Average, 5 = Low, 6 = Bad)
server.allowMultipleConnections TRUE Controls whether a single IP address can be logged in multiple times at the same time
server.allowSinglePlayer TRUE Whether users are allowed to start a game when no other users have joined
server.chatFloodTime 2 Minimimum time (in seconds) a user must wait between sending two chat messages
server.coreThreadpoolSize 5 Core size of the thread pool for user actions.
server.createGameFloodTime 2 Minimum time (in seconds) a user must wait between creating two games
server.idleTimeout 3600 How often a user must engage in the server before being kicked for inactivity (0 to disable)
server.keepAliveTimeout 190 Amount of time (in seconds) the server will wait before deciding a client has disconnected. Note: This is not related to user inactivity
server.lagstatDurationSeconds 60 Duration for lag statistics collection in seconds. This is under active development, please give feedback in the discord instead of overriding this value.
server.maxChatLength 150 Maximum length of chat messages (0 to disable)
server.maxClientNameLength 127 Maximum length of the kaillera client name (must be ≤ 127)
server.maxGameChatLength 320 Maximum length of game chat messages
server.maxGameNameLength 127 Maximum length of a game name (must be ≤ 127)
server.maxGames 0 Maximum number of games that can be created in the server simultaneously (0 to disable)
server.maxPing 1000 Maximum allowed roundtrip ping in milliseconds (must be ≤ 1000)
server.maxQuitMessageLength 100 Maximum length of quit messages
server.maxUserNameLength 30 Maximum length of user names (must be ≤ 30)
server.maxUsers 0 Maximum number of concurrent users logged into the server (0 to disable)
server.switchStatusBytesForBuggyClient FALSE Switch some game/player statuses for a Japanese client "Project 64k 0.13 (01 Aug 2003)" that seems to use different values.
twitter.auth.oAuthAccessToken Twitter OAuth access token
twitter.auth.oAuthAccessTokenSecret Twitter OAuth access token secret
twitter.auth.oAuthConsumerKey Twitter OAuth consumer key
twitter.auth.oAuthConsumerSecret Twitter OAuth consumer secret
twitter.broadcastDelaySeconds 15 Delay from starting a game to when the tweet is posted
twitter.deletePostOnClose FALSE Whether to delete Twitter posts when games close
twitter.enabled FALSE Enable posting to twitter based on game activity
twitter.preventBroadcastNameSuffixes Comma-separated list of name suffixes appearing after a @ in the username to prevent posting to twitter

config/language.properties

This file is used to customize the messages sent by the server to the clients, specifically for when users join the server or join a game:

KailleraServerImpl.LoginMessage.1=Welcome to a new EmuLinkerSF-K Server!
KailleraServerImpl.LoginMessage.2=Here is a second message to show to users when they join.
KailleraServerImpl.JoinGameMessage.1=You joined a game!

If your server is in Spanish, Korean, Portuguese, Japanese, Russian, or Chinese all other messages have been translated for you. Please contact us in the discord about adding new translations if you speak a language not represented.

If you choose to use "custom" translations by setting emulinker.language=custom in config/emulinker.cfg, you will also use this file to manually provide translations for everything listed in messages.properties.

Enabling Twitter integration

You can have the server make a Twitter post when a user opens a game, and either delete it or reply marking it as " closed" when the game starts. At the time of writing this, both options are supported by the Twitter API free tier.

To set it up, configure the following values in config/emulinker.cfg:

# Twitter reporting integration switch. When enabled it will
# broadcast new open games.
twitter.enabled=true

# Delay (in seconds) before sending a tweet.
twitter.broadcastDelaySeconds=20

# Comma-separated list of phrases that, if found in the name
# after a "@", will prevent tweet posting.
# Example username: nue@waiting
twitter.preventBroadcastNameSuffixes=waiting,restart
# If true, will simply delete the tweet when the game starts.
twitter.deletePostOnClose=false

# You will need to make a new Twitter API app and fill in these values.
twitter.auth.oAuthAccessToken=
twitter.auth.oAuthAccessTokenSecret=
twitter.auth.oAuthConsumerKey=
twitter.auth.oAuthConsumerSecret=

With these settings, users whose name ends in @waiting (meaning they are waiting for a specific person to join their game) or @restart (meaning they are restarting the game and are waiting for the same person to join) will not have tweets sent. Similarly, users will be notified and given 20 seconds to type /stop to stop the tweet from sending. After the game starts, the account will respond to the original tweet with the text "(opponent found)".

Server Extensions

This server extends the original Kaillera/EmuLinkerSF protocol and admin toolset with additional features. All extensions are backwards compatible — existing clients behave identically without needing any changes. However, some features are only accessible to users with sufficient access levels (admin, superadmin).

Admin Commands

EmuLinker-K adds the following admin chat commands on top of the standard set. Commands are entered in the server lobby chat.

Note

Optional <reason> arguments are internal only — the reason is recorded in logs and viewable via /info, but is never revealed to the affected user or broadcast to the server.

Command Access Description
/ban <UserID> <minutes> [reason] Moderator+ Temporarily ban a user by UserID. The optional reason is recorded internally.
/silence <UserID> <minutes> [reason] Moderator+ Temporarily silence (mute) a user. The optional reason is recorded internally.
/permaban <UserID> [reason] Admin+ Permanently bans a user. Writes an ipaddress,DENY entry to access.cfg with a comment recording the issuer, timestamp, and optional reason.
/permamute <UserID> [reason] Admin+ Permanently silences a user. Writes a silence entry to access.cfg with a comment recording the issuer, timestamp, and optional reason.
/clear <IP | UserID | Name> Admin+ Clears all temporary bans and silences for a user. Accepts an IP address, UserID number, or exact username (case-insensitive).
/info <IP | UserID | Name> Admin+ Displays a user's access level and any active temporary restrictions (with issuer and reason) as private messages to the requesting admin.

Backwards Compatibility Note

The reason parameter on /ban and /silence is optional and appended after the required arguments. Old client UIs that issue these commands automatically (e.g. "/ban 1 10") will continue to work without modification.

access.cfg Persistent Bans and Silences

The standard access.cfg format is extended with a new silence entry type for persistent mutes:

# Permanent silence issued by AdminName on 2026-01-01T00:00:00
# Reason: Hate speech
silence,10.0.0.42

When /permaban or /permamute is used, the server appends the appropriate rule to access.cfg along with a comment block. The file is automatically reloaded without a server restart.

The access.cfg file is monitored for changes. Any manual edits (e.g., removing a ban entry to lift a permanent ban) will take effect automatically on the next server check.

Legal Disclaimer

Server administrators are solely responsible for ensuring that their data collection, disclosure, and retention practices (including logs) comply with all applicable laws and regulations. The repository maintainers assume no liability for the use of this software.

Contributing

If you're interested in contributing to this project, please read Contributing.md.

About

A high performance netcode server using the Kaillera protocol.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors

Languages