Skip to content

Commit

Permalink
Migrated from clusterio v13 to v16 according to migration guide
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielv123 committed Feb 6, 2024
1 parent 1bb7d05 commit 5e8fa9b
Show file tree
Hide file tree
Showing 54 changed files with 1,159 additions and 993 deletions.
11 changes: 6 additions & 5 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
// Use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon.
"args": { "VARIANT": "ubuntu-22.04" }
},
"workspaceMount": "source=${localWorkspaceFolder},target=/factorioClusterio/external_plugins/gridworld,type=bind",
"workspaceFolder": "/factorioClusterio",
"workspaceMount": "source=${localWorkspaceFolder},target=/clusterio/external_plugins/gridworld,type=bind",
"workspaceFolder": "/clusterio",
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [8080],
// sudo mv /workspaces/gridworld /factorioClusterio/external_plugins/
// sudo mv /workspaces/gridworld /clusterio/external_plugins/
// Use 'postCreateCommand' to run commands after the container is created. wget -O factorio.tar.xz https://www.factorio.com/get-download/latest/headless/linux64 && tar -xf factorio.tar.xz && rm factorio.tar.xz
"postCreateCommand": "mv /workspaces/gridworld/ /workspaces/gridworld2 && cp -r /factorioClusterio/ /workspaces/gridworld && sudo rm -rf /factorioClusterio && mv /workspaces/gridworld2/ /workspaces/gridworld/external_plugins/gridworld && cd /workspaces/gridworld && git pull && pnpm install && wget -O factorio.tar.xz https://www.factorio.com/get-download/latest/headless/linux64 && tar -xf factorio.tar.xz && rm factorio.tar.xz",
"postCreateCommand": "mv /workspaces/gridworld/ /workspaces/gridworld2 && cp -r /clusterio/ /workspaces/gridworld && sudo rm -rf /clusterio && mv /workspaces/gridworld2/ /workspaces/gridworld/external_plugins/gridworld && cd /workspaces/gridworld && git pull && pnpm install && wget -O factorio.tar.xz https://www.factorio.com/get-download/latest/headless/linux64 && tar -xf factorio.tar.xz && rm factorio.tar.xz",

// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
Expand All @@ -28,7 +28,8 @@
"vscode": {
"extensions": [
"yzhang.markdown-all-in-one",
"yinfei.luahelper"
"yinfei.luahelper",
"dbaeumer.vscode-eslint"
]
}
}
Expand Down
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ module.exports = {
"linebreak-style": ["error", "unix"],
"lines-around-comment": "off",
"lines-around-directive": "off",
"lines-between-class-members": ["error", "always"],
"lines-between-class-members": "off",
"max-classes-per-file": "off",
"max-depth": "error",
"max-len": ["error", {
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/diagram.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
uses: actions/checkout@controller
- name: Update diagram
uses: githubocto/repo-visualizer@main
with:
Expand Down
40 changes: 22 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Dependencies:
Run the following commands in the folder Clusterio is installed to:

npm install @danielv123/gridworld
npx clusteriomaster plugin add @danielv123/gridworld
npx clusteriocontroller plugin add @danielv123/gridworld

Substitute clusteriomaster with clusterioslave or clusterioctl if this a dedicated slave or ctl installation respectively.
Substitute clusteriocontroller with clusteriohost or clusterioctl if this a dedicated host or ctl installation respectively.

![Visualization of this repo](./images/diagram.svg)

Expand All @@ -26,29 +26,33 @@ Clone the repository in clusterio/external_plugins/

cd clusterio
pnpm install
node packages/create --dev # Interactive
node packages/ctl plugin add ./external_plugins/gridworld
pnpm install @clusterio/plugin-edge_transports -w
pnpm install @hornwitser/server_select -w
node packages/master/ bootstrap create-admin Danielv123
node packages/master plugin add @clusterio/plugin-edge_transports
node packages/master plugin add @hornwitser/server_select
node packages/master plugin add ./plugins/global_chat
node packages/master plugin add ./plugins/inventory_sync
node packages/master plugin add ./plugins/player_auth
node packages/master plugin add ./plugins/research_sync
node packages/master plugin add ./plugins/statistics_exporter
node packages/master/ bootstrap generate-user-token Danielv123 > token.txt
node packages/master run --dev --dev-plugin gridworld

Log into the webui with the token in token.txt and create a new slave token with id `1` then run the following in terminal to setup the slave:
node packages/controller bootstrap create-ctl-config Danielv123
node packages/controller bootstrap create-admin Danielv123
node packages/controller plugin add @clusterio/plugin-edge_transports
node packages/controller plugin add @hornwitser/server_select
node packages/controller plugin add ./plugins/global_chat
node packages/controller plugin add ./plugins/inventory_sync
node packages/controller plugin add ./plugins/player_auth
node packages/controller plugin add ./plugins/research_sync
node packages/controller plugin add ./plugins/statistics_exporter
node packages/controller bootstrap generate-user-token Danielv123 > token.txt
node packages/controller run --dev --dev-plugin gridworld

Log into the webui with the token in token.txt and create a new host token with id `1` then run the following in terminal to setup the host:

wget -O factorio.tar.gz https://www.factorio.com/get-download/latest/headless/linux64
tar -xf factorio.tar.gz
node packages/slave config set slave.id 1
node packages/slave config set slave.master_token xxxxxxxxxxxxxxxxxxxxx
node packages/slave config set slave.name "Slave 1"
node packages/slave config set slave.public_address "localhost"
node packages/host config set host.id 1
node packages/host config set host.controller_token xxxxxxxxxxxxxxxxxxxxx
node packages/host config set host.name "Host 1"
node packages/host config set host.public_address "localhost"

Luacheck can be downloaded from https://github.com/mpeterv/luacheck/releases/download/0.23.0/luacheck.exe or `sudo apt install lua-check`

Put it in your `%path%` and run `luacheck ./module`

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ1c2VyIiwidXNlciI6IkRhbmllbHYxMjMiLCJpYXQiOjE3MDcwNjYwNDR9.g9iiIDvhX8GrEWGQQYig_smOdf212dmst6HKg9LKU3g
135 changes: 61 additions & 74 deletions master.js → controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
const fs = require("fs-extra");
const path = require("path");

const { libLink, libPlugin, libErrors } = require("@clusterio/lib");
const lib = require("@clusterio/lib");
// eslint-disable-next-line node/no-extraneous-require
const { BaseControllerPlugin } = require("@clusterio/controller");
const registerTileServer = require("./src/routes/tileserver");

// Messages
const messages = require("./messages");
const MigrateInstanceRequest = require("./src/instance_migration/info/MigrateInstanceRequest");

// Message handlers
const getMapDataRequestHandler = require("./src/request_handlers/getMapDataRequestHandler");
const refreshTileDataRequestHandler = require("./src/request_handlers/refreshTileDataRequestHandler");
const setWebSubscriptionRequestHandler = require("./src/request_handlers/setWebSubscriptionRequestHandler");
const startInstanceRequestHandler = require("./src/request_handlers/startInstanceRequestHandler");
const createFactionRequestHandler = require("./src/request_handlers/createFactionRequestHandler");
const updateFactionRequestHandler = require("./src/request_handlers/updateFactionRequestHandler");
const migrateInstanceRequestHandler = require("./src/instance_migration/migrateInstanceRequestHandler");
Expand All @@ -27,7 +33,7 @@ const unclaimServerRequestHandler = require("./src/request_handlers/unclaimServe
const setLoadFactorEventHandler = require("./src/event_handlers/setLoadFactorEventHandler");

async function loadDatabase(config, filename, logger) {
let itemsPath = path.resolve(config.get("master.database_directory"), filename);
let itemsPath = path.resolve(config.get("controller.database_directory"), filename);
logger.verbose(`Loading ${itemsPath}`);
try {
let content = await fs.readFile(itemsPath);
Expand All @@ -42,89 +48,70 @@ async function loadDatabase(config, filename, logger) {
}
}

async function saveDatabase(masterConfig, datastore, filename, logger) {
async function saveDatabase(controllerConfig, datastore, filename, logger) {
if (datastore) {
let file = path.resolve(masterConfig.get("master.database_directory"), filename);
let file = path.resolve(controllerConfig.get("controller.database_directory"), filename);
logger.verbose(`writing ${file}`);
let content = JSON.stringify(Array.from(datastore));
await fs.outputFile(file, content);
}
}

class MasterPlugin extends libPlugin.BaseMasterPlugin {
class ControllerPlugin extends BaseControllerPlugin {
async init() {
this.gridworldDatastore = await loadDatabase(this.master.config, "gridworld.json", this.logger);
this.factionsDatastore = await loadDatabase(this.master.config, "factions.json", this.logger);
this.gridworldDatastore = await loadDatabase(this.controller.config, "gridworld.json", this.logger);
this.factionsDatastore = await loadDatabase(this.controller.config, "factions.json", this.logger);
this.autosaveId = setInterval(() => {
saveDatabase(this.master.config, this.gridworldDatastore, "gridworld.json", this.logger).catch(err => {
saveDatabase(this.controller.config, this.gridworldDatastore, "gridworld.json", this.logger).catch(err => {
this.logger.error(`Unexpected error autosaving gridworld data:\n${err.stack}`);
});
saveDatabase(this.master.config, this.factionsDatastore, "factions.json", this.logger).catch(err => {
saveDatabase(this.controller.config, this.factionsDatastore, "factions.json", this.logger).catch(err => {
this.logger.error(`Unexpected error autosaving factions data:\n${err.stack}`);
});
}, this.master.config.get("gridworld.autosave_interval") * 1000);
}, this.controller.config.get("gridworld.autosave_interval") * 1000);

// Prepare tiles folder
this._tilesPath = path.resolve(
this.master.config.get("master.database_directory"),
this.master.config.get("gridworld.tiles_directory")
this.controller.config.get("controller.database_directory"),
this.controller.config.get("gridworld.tiles_directory")
);
await fs.ensureDir(this._tilesPath);

registerTileServer(this.master.app, this._tilesPath);
registerTileServer(this.controller.app, this._tilesPath);

this.subscribedControlLinks = [];
}

playerPositionEventHandler = playerPositionEventHandler;

getMapDataRequestHandler = getMapDataRequestHandler;

updateEdgeTransportEdgesRequestHandler = updateEdgeTransportEdgesRequestHandler;

createFactionGridRequestHandler = createFactionGridRequestHandler;

refreshTileDataRequestHandler = refreshTileDataRequestHandler;

setWebSubscriptionRequestHandler = setWebSubscriptionRequestHandler;

startInstanceRequestHandler = startInstanceRequestHandler;

createFactionRequestHandler = createFactionRequestHandler;

updateFactionRequestHandler = updateFactionRequestHandler;

migrateInstanceRequestHandler = migrateInstanceRequestHandler;

joinGridworldRequestHandler = joinGridworldRequestHandler;

performEdgeTeleportRequestHandler = performEdgeTeleportRequestHandler;

refreshFactionDataRequestHandler = refreshFactionDataRequestHandler;

factionInvitePlayerRequestHandler = factionInvitePlayerRequestHandler;

joinFactionRequestHandler = joinFactionRequestHandler;

factionChangeMemberRoleRequestHandler = factionChangeMemberRoleRequestHandler;

leaveFactionRequestHandler = leaveFactionRequestHandler;

claimServerRequestHandler = claimServerRequestHandler;

unclaimServerRequestHandler = unclaimServerRequestHandler;

setLoadFactorEventHandler = setLoadFactorEventHandler;
// Register event handlers to messages
this.controller.handle(messages.PlayerPosition, playerPositionEventHandler.bind(this));
this.controller.handle(messages.GetMapData, getMapDataRequestHandler.bind(this));
this.controller.handle(messages.UpdateEdgeTransportEdges, updateEdgeTransportEdgesRequestHandler.bind(this));
this.controller.handle(messages.CreateFactionGrid, createFactionGridRequestHandler.bind(this));
this.controller.handle(messages.RefreshTileData, refreshTileDataRequestHandler.bind(this));
this.controller.handle(messages.SetWebSubscription, setWebSubscriptionRequestHandler.bind(this));
this.controller.handle(messages.CreateFaction, createFactionRequestHandler.bind(this));
this.controller.handle(messages.UpdateFaction, updateFactionRequestHandler.bind(this));
this.controller.handle(MigrateInstanceRequest, migrateInstanceRequestHandler.bind(this));
this.controller.handle(messages.JoinGridworld, joinGridworldRequestHandler.bind(this));
this.controller.handle(messages.PerformEdgeTeleport, performEdgeTeleportRequestHandler.bind(this));
this.controller.handle(messages.RefreshFactionData, refreshFactionDataRequestHandler.bind(this));
this.controller.handle(messages.FactionInvitePlayer, factionInvitePlayerRequestHandler.bind(this));
this.controller.handle(messages.JoinFaction, joinFactionRequestHandler.bind(this));
this.controller.handle(messages.FactionChangeMemberRole, factionChangeMemberRoleRequestHandler.bind(this));
this.controller.handle(messages.LeaveFaction, leaveFactionRequestHandler.bind(this));
this.controller.handle(messages.ClaimServer, claimServerRequestHandler.bind(this));
this.controller.handle(messages.UnclaimServer, unclaimServerRequestHandler.bind(this));
this.controller.handle(messages.SetLoadFactor, setLoadFactorEventHandler.bind(this));
}

async onInstanceStatusChanged(instance) {
if (instance.status === "running") {
let instanceId = instance.config.get("instance.id");
let slaveId = instance.config.get("instance.assigned_slave");
let hostId = instance.config.get("instance.assigned_host");
let x = instance.config.get("gridworld.grid_x_position");
let y = instance.config.get("gridworld.grid_y_position");
let slaveConnection = this.master.wsServer.slaveConnections.get(slaveId);
let instances = [...this.master.instances];
await this.info.messages.populateNeighborData.send(slaveConnection, {
let hostConnection = this.controller.wsServer.hostConnections.get(hostId);
let instances = [...this.controller.instances];
await this.controller.sendTo({ instanceId }, new messages.PopulateNeighborData({
instance_id: instanceId,
north: instances.find(z => z[1].config.get("gridworld.grid_x_position") === x
&& z[1].config.get("gridworld.grid_y_position") === y - 1)?.[0] || null,
Expand All @@ -134,39 +121,39 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin {
&& z[1].config.get("gridworld.grid_y_position") === y)?.[0] || null,
west: instances.find(z => z[1].config.get("gridworld.grid_x_position") === x - 1
&& z[1].config.get("gridworld.grid_y_position") === y)?.[0] || null,
});
}));
}
}

async setInstanceConfigField(instanceId, field, value) {
// Code lifted from ControlConnection.js setInstanceConfigFieldRequestHandler(message)
let instance = this.master.instances.get(instanceId);
let instance = this.controller.instances.get(instanceId);
if (!instance) {
throw new libErrors.RequestError(`Instance with ID ${instanceId} does not exist`);
throw new lib.RequestError(`Instance with ID ${instanceId} does not exist`);
}

if (field === "instance.assigned_slave") {
throw new libErrors.RequestError("instance.assigned_slave must be set through the assign-slave interface");
if (field === "instance.assigned_host") {
throw new lib.RequestError("instance.assigned_host must be set through the assign-host interface");
}

if (field === "instance.id") {
// XXX is this worth implementing? It's race condition galore.
throw new libErrors.RequestError("Setting instance.id is not supported");
throw new lib.RequestError("Setting instance.id is not supported");
}

instance.config.set(field, value, "control");
await this.updateInstanceConfig(instance);
}

async updateInstanceConfig(instance) {
let slaveId = instance.config.get("instance.assigned_slave");
if (slaveId) {
let connection = this.master.wsServer.slaveConnections.get(slaveId);
let hostId = instance.config.get("instance.assigned_host");
if (hostId) {
let connection = this.controller.wsServer.hostConnections.get(hostId);
if (connection) {
await libLink.messages.assignInstance.send(connection, {
instance_id: instance.config.get("instance.id"),
serialized_config: instance.config.serialize("slave"),
});
await connection.send(new lib.InstanceAssignInternalRequest({
instanceId: instance_id,
config: instance.config.toRemote("host"),
}));
}
}
}
Expand All @@ -180,11 +167,11 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin {

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

module.exports = {
MasterPlugin,
ControllerPlugin,
};
22 changes: 11 additions & 11 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Far fetched goals:
The current version of the plugin has the following flow:

1. Installation
1. User clicks "create new gridworld" and enters their preferred grid cell size and the slave used to host the lobby server
1. User clicks "create new gridworld" and enters their preferred grid cell size and the host used to host the lobby server
1. User clicks "create gridworld"
1. The plugin creates a lobby server with a new grid_id
the web map
Expand All @@ -45,16 +45,16 @@ Clone the repository in clusterio/external_plugins/ and run the following:
node packages/ctl plugin add ./external_plugins/gridworld
pnpm install @clusterio/plugin-edge_transports -w
pnpm install @hornwitser/server_select -w
node packages/master/ bootstrap create-admin Danielv123
node packages/master plugin add @clusterio/plugin-edge_transports
node packages/master plugin add @hornwitser/server_select
node packages/master plugin add ./plugins/global_chat
node packages/master plugin add ./plugins/inventory_sync
node packages/master plugin add ./plugins/player_auth
node packages/master plugin add ./plugins/research_sync
node packages/master plugin add ./plugins/statistics_exporter
node packages/master/ bootstrap generate-user-token Danielv123 > token.txt
node packages/master run --dev --dev-plugin gridworld
node packages/controller/ bootstrap create-admin Danielv123
node packages/controller plugin add @clusterio/plugin-edge_transports
node packages/controller plugin add @hornwitser/server_select
node packages/controller plugin add ./plugins/global_chat
node packages/controller plugin add ./plugins/inventory_sync
node packages/controller plugin add ./plugins/player_auth
node packages/controller plugin add ./plugins/research_sync
node packages/controller plugin add ./plugins/statistics_exporter
node packages/controller/ bootstrap generate-user-token Danielv123 > token.txt
node packages/controller run --dev --dev-plugin gridworld

Luacheck can be downloaded from https://github.com/mpeterv/luacheck/releases/download/0.23.0/luacheck.exe

Expand Down
Loading

0 comments on commit 5e8fa9b

Please sign in to comment.