I changed some code and it became authoritative! So now I have a 1v1 match working. I will move on to state and input sync, etc. Then will circle back around to try handling 2v2 or 3v3 with teams/groups/etc. later.
Here is my code, for people who may want some reference later:
Unity ‘MatchService.cs’:
public async Task ConnectToMatchMakerLobby(string mapId)
{
Debug.Log("------->ConnectToMatchMakerLobby");
var stringProperties = new Dictionary<string, string>
{
{"map", mapId}
};
var numericProperties = new Dictionary<string, double>
{
{"team", 0} // possibly used for later ??
};
var socket = refs.Get<ISocket>();
socket.ReceivedMatchmakerMatched += OnReceivedMatchmakerMatched;
var query = "+properties.map:"+mapId;
var matchmakerTicket = await socket.AddMatchmakerAsync(query, 2, 2, stringProperties, numericProperties);
matchTicket = matchmakerTicket.Ticket;
refs.Bind(matchmakerTicket);
}
public async Task DisconnectFromMatchMakerLobby()
{
Debug.Log("------->DisconnectToMatchMakerLobby");
var ticket = refs.Get<IMatchmakerTicket>();
if (ticket != null)
{
var socket = refs.Get<ISocket>();
if (socket != null)
{
socket.ReceivedMatchmakerMatched -= OnReceivedMatchmakerMatched;
await socket.RemoveMatchmakerAsync(ticket);
}
refs.Unbind<IMatchmakerTicket>();
Debug.Log("------->removed matchTicket");
}
}
private async void OnReceivedMatchmakerMatched(IMatchmakerMatched matchmakerMatched)
{
Debug.Log("------->Matchmaker: Found Match (matchmakerMatched):" + matchmakerMatched.MatchId);
refs.Bind(matchmakerMatched);
var socket = refs.Get<ISocket>();
var match = await socket.JoinMatchAsync(matchmakerMatched);
Debug.Log("------->Matchmaker: Connected to Match:" + match.Id);
refs.Bind(match);
socket.ReceivedMatchState += OnReceivedMatchState;
Debug.Log("our session id:" + match.Self.SessionId);
var presences = match.Presences;
foreach (var user in presences)
{
Debug.Log("Other session id:" + user.SessionId);
}
var mainThreadDispatcher = Refs.Instance.Get<MainThreadDispatcher>();
mainThreadDispatcher.OnNextUpdate(() =>
{
var positionData = new PositionData(UnityEngine.Random.Range(-100f, 100f), UnityEngine.Random.Range(-100f, 100f));
var vec3 = positionData.Vec3;
Debug.Log("----------sending state data:" + vec3.x+", "+vec3.z);
SendState(0, positionData.Serialize(positionData));
});
}
private void SendState(long opCode, byte[] state)
{
var socket = refs.Get<ISocket>();
var match = refs.Get<IMatch>();
socket.SendMatchStateAsync(match.Id, opCode, state);
}
‘match_rpc.lua’
local nk = require("nakama")
local function matchmaker_matched(context, matched_users)
local match_id = nk.match_create("matchcontrol", { expected_users = matched_users })
nk.logger_info("match_id:"..match_id)
return match_id
end
nk.register_matchmaker_matched(matchmaker_matched)
‘matchcontrol.lua’
local match_control = {}
function match_control.match_init(context, params) -- state, tickrate, label
local match_state = {
presences = {},
input_buffers = {},
delta_buffers = {},
snapshot_buffers = {}
}
local tick_rate = 10
local label = ""
return match_state, tick_rate, label
end
-- see how to send tick, etc.
function match_control.match_join_attempt(context, dispatcher, tick, state, presence, metadata)
if (state.presences[presence.user_id] ~= nil) then
return state, false, "User already joined."
end
return state, true
end
-- see how to send tick, etc.
function match_control.match_join(context, dispatcher, tick, state, presences)
for _, presence in ipairs(presences) do
state.presences[presence.user_id] = presence
end
return state
end
-- see how to send tick, etc.
function match_control.match_leave(context, dispatcher, tick, state, presences)
for _, presence in ipairs(presences) do
state.presences[presence.user_id] = nil
end
return state
end
-- see how to send tick, etc. validate inputs, positions, etc
function match_control.match_loop(context, dispatcher, tick, state, messages)
--[[for _, p in pairs(state.presences) do
nk.logger_info(string.format("Presence %s named %s", p.user_id, p.username))
end
for _, m in ipairs(messages) do
nk.logger_info(string.format("Received %s from %s", m.data, m.sender.username))
local decoded = nk.json_decode(m.data)
for k, v in pairs(decoded) do
nk.logger_info(string.format("Key %s contains value %s", k, v))
end
-- PONG message back to sender
dispatcher.broadcast_message(1, message.data, { message.sender })
end]]--
return state
end
-- see how to send tick, etc. leave match, play animation, taking players back to menu, cleaning up state
function match_control.match_terminate(context, dispatcher, tick, state, grace_seconds)
local message = "Server shutting down in " .. grace_seconds .. " seconds"
-- dispatcher.broadcast_message(2, message)
-- return nil
return state
end
return match_control