Skip to content

Commit

Permalink
feat(skymp5-server): support RakNet bind address in config & replace …
Browse files Browse the repository at this point in the history
…legacy ip param (#2352)
  • Loading branch information
nic11 authored Feb 26, 2025
1 parent 0735c39 commit fdf6447
Show file tree
Hide file tree
Showing 14 changed files with 64 additions and 96 deletions.
12 changes: 1 addition & 11 deletions docs/docs_server_command_line_api.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
# Server Command Line API

During the very first months of the current generation of skymp server's existence, we used command line API to configure our servers. These days we have [a configuration file](docs_server_configuration_reference.md) in JSON format, but configuring via command-line arguments is also supported.

```bash
--maxPlayers 108 --name "Server X" --port 7777 --ip "127.0.0.1" --offlineMode false
```

Things you need to know about command-line arguments before using one:

- Arguments passed directly to a command line implicitly override values from `server-settings.json` if they are set.
- The only options that exist as command-line arguments are `maxPlayers`, `name`, `port`, `ip`, `offlineMode`. They have the same meanings as options in `server-settings.json`.
- Instead of writing `--maxPlayers 108` you may use `-m 108`. Only `maxPlayers` option has a short version.
During the very first months of the current generation of skymp server's existence, we used command line API to configure our servers. These days we have [a configuration file](docs_server_configuration_reference.md) in JSON format, CLI options are no longer supported.
18 changes: 15 additions & 3 deletions docs/docs_server_configuration_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,26 @@ Server's name that will be published on a master server.
}
```

## ip
## masterKey

This IP-address would be used by player clients to connect to your server. Do not try to type `"0.0.0.0"`, just remove this option from document if you want to use your current public IP.
Specify the server key you wish to use for Master API. Client must have the same key specified to log in successfully.

```json5
{
// ...
"ip": "127.0.0.1"
"masterKey": "my-awesome-server",
// ...
}
```

## listenHost

Specifies the IP address to bind to. Applies to the main UDP traffic (RakNet).

```json5
{
// ...
"listenHost": "127.0.0.1",
// ...
}
```
Expand Down
15 changes: 9 additions & 6 deletions skymp5-server/cpp/addon/ScampServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,9 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info)
partOne = std::make_shared<PartOne>();
listener = std::make_shared<ScampServerListener>(*this);
partOne->AddListener(listener);
Napi::Number port = info[0].As<Napi::Number>(),
maxConnections = info[1].As<Napi::Number>();

std::string serverSettingsJson =
static_cast<std::string>(info[2].As<Napi::String>());
static_cast<std::string>(info[0].As<Napi::String>());

serverMock = std::make_shared<Networking::MockServer>();

Expand All @@ -178,6 +176,12 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info)

auto serverSettings = nlohmann::json::parse(serverSettingsJson);

// TODO: rework parsing with archives?
std::string listenHost =
serverSettings.at("listenHost").get<std::string>();
uint32_t listenPort = serverSettings.at("port").get<uint32_t>();
uint32_t maxPlayers = serverSettings.at("maxPlayers").get<uint32_t>();

if (serverSettings.find("weaponStaminaModifiers") !=
serverSettings.end()) {
if (serverSettings.at("weaponStaminaModifiers").is_object()) {
Expand Down Expand Up @@ -317,9 +321,8 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info)
? std::string(kNetworkingPasswordPrefix) +
static_cast<std::string>(serverSettings["password"])
: std::string(kNetworkingPasswordPrefix);
auto realServer = Networking::CreateServer(
static_cast<uint32_t>(port), static_cast<uint32_t>(maxConnections),
password.data());
auto realServer = Networking::CreateServer(listenHost.c_str(), listenPort,
maxPlayers, password.data());

static_assert(kMockServerIdx == 1);
server = Networking::CreateCombinedServer({ realServer, serverMock });
Expand Down
12 changes: 7 additions & 5 deletions skymp5-server/cpp/mp_common/Networking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ class Server : public Networking::IServer
public:
constexpr static int timeoutTimeMs = 60000;

Server(unsigned short port_, unsigned short maxConnections,
const char* password_)
Server(const char* listenAddress, unsigned short port_,
unsigned short maxConnections, const char* password_)
: password(password_)
{
if (maxConnections > kMaxPlayers) {
Expand All @@ -141,7 +141,7 @@ class Server : public Networking::IServer

idManager = std::make_unique<IdManager>(maxConnections);
peer = std::make_unique<RakPeer>();
socket = std::make_unique<SocketDescriptor>(port_, nullptr);
socket = std::make_unique<SocketDescriptor>(port_, listenAddress);

const auto res = peer->Startup(maxConnections, &*socket, 1);
if (res != StartupResult::RAKNET_STARTED) {
Expand Down Expand Up @@ -218,9 +218,11 @@ std::shared_ptr<Networking::IClient> Networking::CreateClient(
}

std::shared_ptr<Networking::IServer> Networking::CreateServer(
unsigned short port, unsigned short maxConnections, const char* password)
const char* listenAddress, unsigned short port,
unsigned short maxConnections, const char* password)
{
return std::make_shared<Server>(port, maxConnections, password);
return std::make_shared<Server>(listenAddress, port, maxConnections,
password);
}

void Networking::HandlePacketClientside(Networking::IClient::OnPacket onPacket,
Expand Down
4 changes: 3 additions & 1 deletion skymp5-server/cpp/mp_common/Networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ namespace Networking {
std::shared_ptr<IClient> CreateClient(const char* serverIp,
unsigned short serverPort, int timeoutMs,
const char* password);
std::shared_ptr<IServer> CreateServer(unsigned short port,

std::shared_ptr<IServer> CreateServer(const char* listenAddress,
unsigned short port,
unsigned short maxConnections,
const char* password);

Expand Down
13 changes: 6 additions & 7 deletions skymp5-server/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,17 @@ const setupStreams = (scampNative: any) => {
const main = async () => {
const settingsObject = await Settings.get();
const {
port, master, maxPlayers, name, ip, offlineMode, gamemodePath
port, master, maxPlayers, name, masterKey, offlineMode, gamemodePath
} = settingsObject;

const log = console.log;
const systems = new Array<System>();
systems.push(
new MasterClient(log, port, master, maxPlayers, name, ip, 5000, offlineMode),
new MasterClient(log, port, master, maxPlayers, name, masterKey, 5000, offlineMode),
new Spawn(log),
new Login(log, maxPlayers, master, port, ip, offlineMode),
new Login(log, maxPlayers, master, port, masterKey, offlineMode),
new DiscordBanSystem(),
new MasterApiBalanceSystem(log, maxPlayers, master, port, ip, offlineMode)
new MasterApiBalanceSystem(log, maxPlayers, master, port, masterKey, offlineMode),
);

setupStreams(scampNative.getScampNative());
Expand All @@ -148,10 +148,9 @@ const main = async () => {
let server: any;

try {
server = createScampServer(port, maxPlayers, settingsObject.allSettings);
server = createScampServer(settingsObject.allSettings);
ui.setServer(server);
}
catch (e) {
} catch (e) {
console.error(e);
console.error(`Stopping the server due to the previous error`);
process.exit(-1);
Expand Down
8 changes: 0 additions & 8 deletions skymp5-server/ts/publicIp.ts

This file was deleted.

4 changes: 2 additions & 2 deletions skymp5-server/ts/scampNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export interface ScampServer {
writeLogs(logLevel: string, message: string): void;
}

export const createScampServer = (serverPort: number, maxPlayers: number, serverSettings: Record<string, unknown>) => {
const res = new scampNativeNode.ScampServer(serverPort, maxPlayers, JSON.stringify(serverSettings));
export const createScampServer = (serverSettings: Record<string, unknown>) => {
const res = new scampNativeNode.ScampServer(JSON.stringify(serverSettings));
res._setSelf(res);
return res;
}
Expand Down
21 changes: 2 additions & 19 deletions skymp5-server/ts/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface DiscordAuthSettings {
}

export class Settings {
ip: string | null = null;
masterKey: string | null = null;
port = 7777;
maxPlayers = 100;
master: string = "https://gateway.skymp.net";
Expand All @@ -39,19 +39,8 @@ export class Settings {
static async get(): Promise<Settings> {
if (!Settings.cachedPromise) {
Settings.cachedPromise = (async () => {
const args = Settings.parseArgs();
const res = new Settings();

await res.loadSettings(); // Load settings asynchronously

// Override settings with command line arguments if available
res.port = +args['port'] || res.port;
res.maxPlayers = +args['maxPlayers'] || res.maxPlayers;
res.master = args['master'] || res.master;
res.name = args['name'] || res.name;
res.ip = args['ip'] || res.ip;
res.offlineMode = args['offlineMode'] || res.offlineMode;

return res;
})();
}
Expand All @@ -68,7 +57,7 @@ export class Settings {

const settings = await fetchServerSettings();
[
'ip',
'masterKey',
'port',
'maxPlayers',
'master',
Expand All @@ -91,12 +80,6 @@ export class Settings {
add_help: false,
description: '',
});
parser.add_argument('-m', '--maxPlayers');
parser.add_argument('--master');
parser.add_argument('--name');
parser.add_argument('--port');
parser.add_argument('--ip');
parser.add_argument('--offlineMode');
return parser.parse_args();
}

Expand Down
15 changes: 4 additions & 11 deletions skymp5-server/ts/systems/login.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { System, Log, Content, SystemContext } from "./system";
import { getMyPublicIp } from "../publicIp";
import { Settings } from "../settings";
import * as fetchRetry from "fetch-retry";

Expand Down Expand Up @@ -28,7 +27,7 @@ export class Login implements System {
private maxPlayers: number,
private masterUrl: string | null,
private serverPort: number,
private ip: string,
private masterKey: string,
private offlineMode: boolean
) { }

Expand All @@ -48,7 +47,7 @@ export class Login implements System {

private async getUserProfile(session: string, userId: number, ctx: SystemContext): Promise<UserProfile> {
const response = await this.fetchRetry(
`${this.masterUrl}/api/servers/${this.myAddr}/sessions/${session}`,
`${this.masterUrl}/api/servers/${this.masterKey}/sessions/${session}`,
this.getFetchOptions('getUserProfile')
);

Expand All @@ -71,13 +70,8 @@ export class Login implements System {
async initAsync(ctx: SystemContext): Promise<void> {
this.settingsObject = await Settings.get();

if (this.ip && this.ip != "null") {
this.myAddr = this.ip + ":" + this.serverPort;
} else {
this.myAddr = (await getMyPublicIp()) + ":" + this.serverPort;
}
this.log(
`Login system assumed that ${this.myAddr} is our address on master`
`Login system assumed that ${this.masterKey} is our master api key`
);
}

Expand All @@ -93,8 +87,7 @@ export class Login implements System {
if (type !== "loginWithSkympIo") return;

const ip = ctx.svr.getUserIp(userId);
const guid = ctx.svr.getUserGuid(userId);
console.log(`Connecting a user ${userId} with ip ${ip}, guid ${guid}`);
console.log(`Connecting a user ${userId} with ip ${ip}`);

let discordAuth = this.settingsObject.discordAuth;

Expand Down
15 changes: 4 additions & 11 deletions skymp5-server/ts/systems/masterApiBalanceSystem.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { System, Log } from "./system";
import Axios from "axios";
import { SystemContext } from "./system";
import { getMyPublicIp } from "../publicIp";
import { Settings } from "../settings";

export class MasterApiBalanceSystem implements System {
Expand All @@ -12,7 +11,7 @@ export class MasterApiBalanceSystem implements System {
private maxPlayers: number,
private masterUrl: string | null,
private serverPort: number,
private ip: string,
private masterKey: string,
private offlineMode: boolean) {
this.sessionByUserId = new Array<string | undefined>(this.maxPlayers);
}
Expand All @@ -24,13 +23,8 @@ export class MasterApiBalanceSystem implements System {
};
ctx.gm.on("userAssignSession", listenerFn);

if (this.ip && this.ip != "null") {
this.myAddr = this.ip + ":" + this.serverPort;
} else {
this.myAddr = (await getMyPublicIp()) + ":" + this.serverPort;
}
this.log(
`MasterApiBalanceSystem system assumed that ${this.myAddr} is our address on master`
`MasterApiBalanceSystem system assumed that ${this.masterKey} is our address on master`,
);

// Effectively makes mp.getUserMasterApiBalance & mp.makeUserMasterApiPurchase a part of gamemode API
Expand Down Expand Up @@ -65,7 +59,7 @@ export class MasterApiBalanceSystem implements System {
private async getUserBalanceImpl(session: string): Promise<number> {
try {
const response = await Axios.get(
`${this.masterUrl}/api/servers/${this.myAddr}/sessions/${session}/balance`
`${this.masterUrl}/api/servers/${this.masterKey}/sessions/${session}/balance`,
);
if (!response.data || !response.data.user || !response.data.user.id || typeof response.data.user.balance !== "number") {
throw new Error(`getUserBalanceImpl: bad master-api response ${JSON.stringify(response.data)}`);
Expand All @@ -86,7 +80,7 @@ export class MasterApiBalanceSystem implements System {
}

const response = await Axios.post(
`${this.masterUrl}/api/servers/${this.myAddr}/sessions/${session}/purchase`,
`${this.masterUrl}/api/servers/${this.masterKey}/sessions/${session}/purchase`,
{ balanceToSpend },
{ headers: { 'X-Auth-Token': authToken } }
);
Expand All @@ -109,6 +103,5 @@ export class MasterApiBalanceSystem implements System {
this.sessionByUserId[userId] = undefined;
}

private myAddr: string;
private sessionByUserId: Array<string | undefined>;
}
9 changes: 2 additions & 7 deletions skymp5-server/ts/systems/masterClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { System, Log } from "./system";
import Axios from "axios";
import { SystemContext } from "./system";
import { getMyPublicIp } from "../publicIp";
import { ScampServer } from "../scampNative";

export class MasterClient implements System {
Expand All @@ -13,7 +12,7 @@ export class MasterClient implements System {
private masterUrl: string | null,
private maxPlayers: number,
private name: string,
private ip: string,
private masterKey: string,
private updateIntervalMs = 5000,
private offlineMode = false
) { }
Expand All @@ -23,11 +22,7 @@ export class MasterClient implements System {

this.log(`Using master server on ${this.masterUrl}`);

let myAddr: string;
if (this.ip && this.ip != "null") myAddr = this.ip + ":" + this.serverPort;
else myAddr = (await getMyPublicIp()) + ":" + this.serverPort;

this.endpoint = `${this.masterUrl}/api/servers/${myAddr}`;
this.endpoint = `${this.masterUrl}/api/servers/${this.masterKey}`;
this.log(`Our endpoint on master is ${this.endpoint}`);
}

Expand Down
Loading

0 comments on commit fdf6447

Please sign in to comment.