Network
network is for an experience made of more than one server. Such an experience is a network. Each server in it is a member. One member is the default: players land on it. Every other member runs only when a pack asks for an instance of it.
An instance has a label. One member can run in several ways, such as 2v2 and 4v4. The label says which. Minefort stores the label and filters on it, and does nothing else with it.
Any server of the network can call network. The pack names a member and a label. Minefort decides the network, the instance’s id and where the instance runs.
-- The default member: a place where players pick how to play.
-- This author calls an instance of "bedwars-arena" an arena.
local waiting = {} -- instance id -> the players who go there when it is online
commands.add("join <label:word>", function(player, args)
local arena, why = network.find{
label = args.label, -- "2v2"
party = player, -- room for the player's whole party
start = { member = "bedwars-arena", slots = 8, data = { map = "castle" } },
}
if not arena then return "No arena now: " .. tostring(why) end
if arena.state == "online" then
player:connect(arena.id)
else
-- It is still starting. Keep the player here until it is online.
waiting[arena.id] = waiting[arena.id] or {}
table.insert(waiting[arena.id], player)
end
end)
network.on_instance_change(function(instance)
if instance.state ~= "online" then return end
for _, player in waiting[instance.id] or {} do player:connect(instance.id) end
waiting[instance.id] = nil
end)
network.on_instance_stop(function(id) waiting[id] = nil end)
-- The member "bedwars-arena": it says how much room it has.
local me = network.info() -- `me.id` is nil on a server that is not an instance
local map = me and me.data and me.data.map or "castle"
players.on_join(function()
if me and me.id then network.status{ open = math.max(0, me.slots - #players.list()), status = "waiting" } end
end)
-- When the round starts: network.status{ open = 0, status = "playing" }
-- When the round ends, each player goes back to the default member:
-- player:connect(me.network) -- the name of the network
The calls:
network.start{ member, label, slots, data? }starts an instance ofmember. It returns{id, state}at once, withstate = "starting": the instance comes online later.slotsis the number of players the instance takes.datais the pack’s own value for the instance, read there withnetwork.info().network.find{ label, players?, party?, start? }returns{id, state}for the instance to sendplayersplayers to, or nil when no instance has room.- It picks an
onlineinstance with that label and that muchopen, the fullest first. - The room is held for 10 seconds, so two calls do not both get the last place.
playersis 1 unless you set it.partyis a player, or their uuid. When they lead a/party, Minefort holds room for every member. Connect the leader, and the party follows them there. A player who leads no party needs room for one.- With
start = { member, slots, data? }, Minefort starts an instance when none has room, and holds the room on it. Thestateis then"starting". - Use
findwithstart, notfindand thenstart. The default runs on many servers, and each would start its own instance.
- It picks an
network.stop(id)stops an instance of this network. It returnstruewhen Minefort accepted it.network.info()returns{network}on every server of a network.networkis the name of the network.- On an instance it returns
{network, id, member, label, slots, data}. idis nil on a server that is not an instance.
- On an instance it returns
network.status{ open, status?, meta? }says where this instance is. It returns nothing and does not yield.openis the number of players the instance takes now.0closes it tofind.statusis the pack’s own word, such as"waiting"or"playing".metais the pack’s own value. Other servers of the network read it.- Minefort takes one status a second. The first call goes at once. Of the calls in the second after it, only the last goes.
network.on_instance_start(fn)callsfn(instance)when an instance is first seen: started, or running when this server began to listen. It returns a connection.network.on_instance_change(fn)callsfn(instance)when an instance’sstate,open,statusormetachanges. It returns a connection.network.on_instance_stop(fn)callsfn(id)when an instance is gone. It returns a connection.network.online(label?)returns how many play on this network: on the platform’s hub, everyone on Minefort; on any other network, its members and their instances. Withlabel, only the players in that mode’s games, every region’s. An answer is kept for 5 seconds, so a scoreboard can ask on every redraw. It returns nil and why when Minefort did not answer.network.instances(label?)returns everything this network runs, as this experience sees it: first its own servers that run all the time, online and taking players, as{id, name, state, players, region}(on the platform’s hub, the hubs), then the network’s games started on demand, each the instance table below. Withlabel, only that mode’s games:network.instances('4v4v4v4').idis whatplayer:connecttakes. Never another network’s. It returns nil and why when Minefort did not answer.network.browse(view, options?)finds other networks, and only the platform’s hub may ask:viewis"search"(what matchesquery: names as they are typed, and domains),"for-you"(more of whatplayerplays, busiest first, none of their recent ones),"recent"(whereplayerplayed last, newest first) or"featured"(the busiest). Each row is a network:{id, name, category, motd, icon, players, maxPlayers, state}, andidis whatplayer:connecttakes. A network is one row, however many servers it has. It returns one page of 6 rows and how many there are in all:local rows, total = network.browse('featured', { page = 2 }).optionsis{ player?, query?, page?, category?, online?, ids?, sort?, also? }.playeris a player or a uuid, and only"for-you"and"recent"need it.pageis from 1, and 1 when left out.- Minefort narrows and orders before it pages, so a page and the total always agree:
category("pvp","survival"…),online = truefor what is up now,ids(at most 64 row ids, such as a player’s favorites) andsort("players"or"name"; the view’s own order when left out)."recent"takes none of these. also = { recent = true, favorites = ids }brings a board’s sidebar in the same round trip, as a third value:local rows, total, side = network.browse('for-you', { player = p, also = { recent = true, favorites = saved } }).side.recentandside.favoritesare each{ rows, total };side.recentisfalsefor a player who is not on this hub.- On a refusal it returns nil and why:
"unsupported"anywhere but the platform’s hub,"not-here"for a player who is not on this hub,"unavailable"when Minefort’s search is down, or"timeout". It is a round trip to Minefort, so call it from a script or callback.
An instance table:
| Field | Set by | Meaning |
|---|---|---|
id |
Minefort | The instance’s name. player:connect takes it. |
member, label |
the pack that started it | Which member, and which label. |
state |
Minefort | "starting", then "online", then "stopping". |
open |
the instance | The number of players it takes now. |
status |
the instance | Its own word, or nil. |
players |
Minefort | The number of players on it. |
meta |
the instance | Its own value, or nil. |
network does not move players. player:connect(id) does. The instance’s id is the server name it takes. The name of the network, network.info().network, sends a player back to the default member. Do not send a player to an instance that is "starting": keep the player until the instance is "online".
Events come to the default member’s servers only. Call network.instances() once at the start, then keep the list from on_instance_start, on_instance_change and on_instance_stop. An event can be lost. Call network.instances() again, once a minute at most, to repair the list.
A failed call returns nil and a reason. start, find and info return nil, reason. list returns {}, reason. stop returns false, reason. The reason is Minefort’s word, or one of these:
"unavailable": there is no network here. Every local run answers this."io": the request was not sent."timeout": no answer came in 35 seconds. Astartmay have happened.
find returns nil with no reason when no instance has room.
Limits. A value out of these is an error, and nothing is sent:
label: 1 to 32 characters ofa-z,0-9and-.member: 1 to 40 characters ofa-z,0-9and-.slots: 1 to 200.players: 1 to 64.open: 0 to 200.data: at most 4096 bytes as JSON.meta: at most 512 bytes as JSON.status: at most 32 characters.- An unknown field in a table is an error.
- A network has at most 20 instances in a region, and one
starta second. - Minefort removes an instance with no players for 60 seconds after its first player, or for 5 minutes without one.
Calls yield, like stores, but for status and the three events. Call from scripts, callbacks or tasks.