Skip to content

Commit

Permalink
Create spawn instance on demand from lobby server
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielv123 authored Sep 12, 2022
1 parent 43868f1 commit a997fd5
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ dist/

# Binary files
bin/

# Log files
*.log
4 changes: 2 additions & 2 deletions gridworld.code-workspace
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"folders": [
{
"path": "/factorioClusterio/external_plugins/gridworld"
"path": "."
},
{
"path": "/factorioClusterio"
"path": "../.."
}
],
"settings": {}
Expand Down
17 changes: 17 additions & 0 deletions info.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,23 @@ module.exports = {
},
},
}),
joinGridworld: new libLink.Request({
type: "gridworld:join_gridworld",
links: ["instance-slave", "slave-master"],
forwardTo: "master",
requestProperties: {
player_name: { type: "string" },
grid_id: { type: "integer" },
},
responseRequired: ["ok", "message"],
responseProperties: {
ok: { type: "boolean" },
message: { type: "string" },
connection_address: { type: "string" },
server_name: { type: "string" },
server_description: { type: "string" },
},
}),
getTileData: new libLink.Request({
type: "gridworld:get_tile_data",
links: ["master-slave", "slave-instance"],
Expand Down
24 changes: 24 additions & 0 deletions instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin {
`Error updating faction:\n${err.stack}`
));
});
this.instance.server.on("ipc-gridworld:join_gridworld", data => {
this.joinGridworld(data).catch(err => this.logger.error(
`Error joining gridworld:\n${err.stack}`
));
});
}

async onStart() {
Expand Down Expand Up @@ -176,6 +181,25 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin {
await this.sendRcon(`/sc gridworld.open_faction_admin_screen("${data.player_name}","${data.faction_id}")`);
}

async joinGridworld(data) {
// Show received progress in game
await this.sendRcon(`/sc gridworld.show_progress("${data.player_name}", "Finding server", "Loading world", 1, 3)`);

let response = await this.info.messages.joinGridworld.send(this.instance, {
player_name: data.player_name,
grid_id: this.instance.config.get("gridworld.grid_id"),
});

// Show completed progress in game
await this.sendRcon(`/sc gridworld.show_progress("${data.player_name}", "Joining gridworld", "${response.message}", 3, 3)`);
await new Promise(r => setTimeout(r, 500));

// Connect to new server
const command = `/sc game.players["${data.player_name}"].connect_to_server({address="${response.connection_address}", name="${response.server_name}", description="${response.server_description}"})`;
// this.logger.info(`Sending command to server: ${command}`);
await this.sendRcon(command);
}

async getTileDataRequestHandler(message) {
if (this.instance.status !== "running") {
throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} is not running ${this.instance.status}`);
Expand Down
4 changes: 4 additions & 0 deletions logs/cluster/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"version": 0,
"files": {}
}
11 changes: 7 additions & 4 deletions master.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ const { libLink, libPlugin, libErrors } = require("@clusterio/lib");
const registerTileServer = require("./src/routes/tileserver");

const getMapDataRequestHandler = require("./src/request_handlers/getMapDataRequestHandler");
const createRequestHandler = require("./src/request_handlers/createRequestHandler");
const refreshTileDataRequestHandler = require("./src/request_handlers/refreshTileDataRequestHandler");
const setPlayerPositionSubscriptionRequestHandler = require("./src/request_handlers/setPlayerPositionSubscriptionRequestHandler");
const startInstanceRequestHandler = require("./src/request_handlers/startInstanceRequestHandler");
const createFactionRequestHandler = require("./src/request_handlers/createFactionRequestHandler");
const updateFactionRequestHandler = require("./src/request_handlers/updateFactionRequestHandler");
const migrateInstanceCommandRequestHandler = require("./src/instance_migration/migrateInstanceCommandRequestHandler");

const playerPositionEventHandler = require("./src/event_handlers/playerPositionEventHandler");
const createFactionGridRequestHandler = require("./src/request_handlers/createFactionGridRequestHandler");
const joinGridworldRequestHandler = require("./src/request_handlers/joinGridworldRequestHandler");

async function loadDatabase(config, filename, logger) {
let itemsPath = path.resolve(config.get("master.database_directory"), filename);
Expand Down Expand Up @@ -70,7 +70,7 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin {

getMapDataRequestHandler = getMapDataRequestHandler;

createRequestHandler = createRequestHandler;
createRequestHandler = createFactionGridRequestHandler;

refreshTileDataRequestHandler = refreshTileDataRequestHandler;

Expand All @@ -84,6 +84,8 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin {

migrateInstanceCommandRequestHandler = migrateInstanceCommandRequestHandler;

joinGridworldRequestHandler = joinGridworldRequestHandler;

async onInstanceStatusChanged(instance) {
if (instance.status === "running") {
let instanceId = instance.config.get("instance.id");
Expand Down Expand Up @@ -147,7 +149,8 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin {

async onShutdown() {
clearInterval(this.autosaveId);
await saveDatabase(this.master.config, this.gridworldDatastore, this.logger);
await saveDatabase(this.master.config, this.gridworldDatastore, "gridworld.json", this.logger);
await saveDatabase(this.master.config, this.factionsDatastore, "factions.json", this.logger);
}
}

Expand Down
3 changes: 3 additions & 0 deletions module/gridworld.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ gridworld.events[clusterio_api.events.on_server_startup] = function()
end
if global.gridworld.neighbor_data == nil then
global.gridworld.neighbor_data = {}
end
if global.gridworld.factions == nil then
global.gridworld.factions = {}
end
end
gridworld.events[defines.events.on_player_joined_game] = function(event)
Expand Down
6 changes: 5 additions & 1 deletion module/lobby/gui/dialog_welcome/events.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
local random_string = require("modules/gridworld/util/random_string")
local dialog_new_game_draw = require("modules/gridworld/lobby/gui/dialog_new_game/draw")
local clusterio_api = require("modules/clusterio/api")

local function on_gui_click(_, action, player)
if player == nil then return end
if action.action == "join_latest_server" then
game.print("Joining server...")
game.print("Joining server...")
clusterio_api.send_json("gridworld:join_gridworld", {
player_name = player.name,
})
end

if action.action == "open_new_game_dialog" then
Expand Down
3 changes: 1 addition & 2 deletions module/util/gui/show_progress/draw.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ local function draw_show_progress(player_name, header, text, progress, max_progr
type = "flow",
{
type = "progressbar",
value = progress,
max_value = max_progress,
value = progress / max_progress,
style = "frame_progressbar",
},
{
Expand Down
1 change: 1 addition & 0 deletions run_master.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(cd ../.. && node packages/master run --dev --dev-plugin gridworld)
1 change: 1 addition & 0 deletions run_slave.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(cd ../.. && node packages/slave run)
97 changes: 97 additions & 0 deletions src/request_handlers/joinGridworldRequestHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"use strict";
const { libLink } = require("@clusterio/lib");
const mapFilter = require("../util/mapFilter");
const mapFind = require("../util/mapFind");

const createServer = require("../worldgen/factionGrid/createServer");
const worldPositionToInstance = require("../worldgen/util/worldPositionToInstance");

module.exports = async function joinGridworldRequestHandler(message, request, link) {
const player_name = message.data.player_name;

// Get player profile from master
const player = this.master.userManager.users.get(player_name);

// Get player faction
const faction = mapFind(this.factionsDatastore, f => f.members.find(member => member.name === player_name));

// Get all instances in the current grid
const instances = mapFilter(this.master.instances, instance => instance.config.get("gridworld.grid_id") === message.data.grid_id);

const response = {
ok: false,
message: "Server not found",
};
let instance_to_connect_to = null;

let last_visited_instance;
let last_visited_instance_time;
for (let instance of instances) {
const instance_id = instance[1].config.get("instance.id");
const instance_stats = player.instanceStats.get(instance_id);
if (Math.max(instance_stats.last_join_at, instance_stats.last_leave_at) > last_visited_instance_time) {
last_visited_instance = instance[1];
last_visited_instance_time = Math.max(instance_stats.last_join_at, instance_stats.last_leave_at);
}
}
if (last_visited_instance) {
// Join the last visited instance
response.ok = true;
response.message = "Joining last visited instance";
response.server_name = last_visited_instance.config.get("instance.name");
response.server_description = "Last visited instance";
instance_to_connect_to = last_visited_instance;
this.logger.info(`Sending player ${player_name} to last visited instance ${instance_to_connect_to.config.get("instance.id")}`);
} else if (faction && 1 === 2) { // TODO: Add option to join faction server
// If the player doesn't have a last known location, send them to their faction spawn

} else {
// If the player doesn't have a faction, send them to the factionless spawn
// Factionless spawn is 25,25
const position = worldPositionToInstance(25, 25, message.data.grid_id, this.master.instances);
if (!position.instance) {
// No instance found for factionless spawn, create a new one
const instance_id = (await createServer({
plugin: this,
x: position.grid_x_position,
y: position.grid_y_position,
grid_id: message.data.grid_id,
})).instanceId;
const instance = this.master.instances.get(instance_id);

response.ok = true;
response.message = "Created new instance";
response.server_name = instance.config.get("instance.name");
response.server_description = "Newly created world spawn";
instance_to_connect_to = instance;
this.logger.info(`Sending player ${player_name} to newly created factionless spanwn at ${instance_to_connect_to.config.get("instance.id")}`);
} else {
// Instance found for factionless spawn, join it
response.ok = true;
response.message = "Joining factionless spawn";
response.server_name = position.instance.config.get("instance.name");
response.server_description = "Factionless spawn";
instance_to_connect_to = position.instance;
this.logger.info(`Sending player ${player_name} to factionless spanwn at ${instance_to_connect_to.config.get("instance.id")}`);
}
}

// Ensure the instance we are connecting to is started
if (instance_to_connect_to) {
const slaveId = instance_to_connect_to.config.get("instance.assigned_slave");
let slaveConnection = this.master.wsServer.slaveConnections.get(slaveId);
// Instance status
if (instance_to_connect_to.status !== "running") {
await libLink.messages.startInstance.send(slaveConnection, {
instance_id: instance_to_connect_to.config.get("instance.id"),
save: null,
});
}

const slave = this.master.slaves.get(slaveId);
response.connection_address = `${slave.public_address}:${instance_to_connect_to.game_port}`;
}

// Return response to client
return response;
};
16 changes: 16 additions & 0 deletions src/util/mapFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use strict";
/**
* Equivalent to Array.prototype.filter, but for Maps.
* @param {Map<any, any>} map Map to filter
* @param {function(value):boolean} predicate Filtering function
* @returns {Map<any, any>} Filtered map
*/
module.exports = function mapFilter(map, predicate) {
const newMap = new Map();
for (const [key, value] of map) {
if (predicate(value)) {
newMap.set(key, value);
}
}
return newMap;
};
15 changes: 15 additions & 0 deletions src/util/mapFind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use strict";
/**
* Equivalent to Array.prototype.find, but for Maps.
* @param {Map} map The map to search.
* @param {function(value):boolean} predicate The predicate function, looks for truthy values
* @returns {any} value of first element in map that satisfies predicate
*/
module.exports = function mapFind(map, predicate) {
for (const [_, value] of map) {
if (predicate(value)) {
return value;
}
}
return undefined;
};
1 change: 1 addition & 0 deletions src/worldgen/createInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = async function createInstance(plugin, name, x, y, x_size, y_siz
let instanceConfig = new libConfig.InstanceConfig("master");
await instanceConfig.init();
instanceConfig.set("instance.name", name);
instanceConfig.set("gridworld.grid_id", grid_id);
instanceConfig.set("gridworld.grid_x_position", x);
instanceConfig.set("gridworld.grid_y_position", y);
instanceConfig.set("gridworld.grid_x_size", x_size);
Expand Down
4 changes: 2 additions & 2 deletions src/worldgen/createLobbyServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ module.exports = async function createLobbyServer(plugin, slaveId, x_size, y_siz
instanceConfig.set("instance.auto_start", true);
instanceConfig.set("gridworld.is_lobby_server", true);
instanceConfig.set("gridworld.grid_id", Math.ceil(Math.random() * 1000));
instanceConfig.set("gridworld.x_size", x_size);
instanceConfig.set("gridworld.y_size", y_size);
instanceConfig.set("gridworld.grid_x_size", x_size);
instanceConfig.set("gridworld.grid_y_size", y_size);

let instanceId = instanceConfig.get("instance.id");
if (plugin.master.instances.has(instanceId)) {
Expand Down
Loading

0 comments on commit a997fd5

Please sign in to comment.