Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save creepteks/adfcc8888c53e7119b9c02a3c2adcfb8 to your computer and use it in GitHub Desktop.

Select an option

Save creepteks/adfcc8888c53e7119b9c02a3c2adcfb8 to your computer and use it in GitHub Desktop.

Revisions

  1. creepteks renamed this gist Jul 20, 2020. 1 changed file with 0 additions and 0 deletions.
  2. creepteks created this gist Jul 20, 2020.
    5 changes: 5 additions & 0 deletions description.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    I created a simple authoritave backend with support for matchmaking using the following sample:
    https://github.com/heroiclabs/nakama/blob/master/sample_go_module/sample.go

    I also used the following gist to create a minimal setup for the development:
    https://gist.github.com/novabyte/cc6d57022e2d3baa40e98d8fc91dc4c8
    144 changes: 144 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    // Licensed under the Apache License, Version 2.0 (the "License");
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at
    //
    // http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.

    package main

    import (
    "context"
    "database/sql"
    "encoding/json"

    "github.com/heroiclabs/nakama-common/runtime"
    )
    func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {

    logger.Info("backend loaded.")

    if err := initializer.RegisterMatch("PvP", CreateMatchInternal); err != nil {
    return err
    }

    if err := initializer.RegisterMatchmakerMatched(DoMatchmaking); err != nil {
    logger.Error("Unable to register: %v", err)
    return err
    }
    return nil
    }

    func DoMatchmaking(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, entries []runtime.MatchmakerEntry) (string, error) {
    for _, e := range entries {
    logger.Info("Matched user '%s' named '%s'", e.GetPresence().GetUserId(), e.GetPresence().GetUsername())
    for k, v := range e.GetProperties() {
    logger.Info("Matched on '%s' value '%v'", k, v)
    }
    }

    matchId, err := nk.MatchCreate(ctx, "davaaPvP", map[string]interface{}{"invited": entries, "debug": true})
    if err != nil {
    return "", err
    }

    return matchId, nil
    }

    func CreateMatchInternal(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule) (runtime.Match, error) {
    return &Match{}, nil
    }

    type MatchState struct {
    debug bool
    presences map[string]runtime.Presence
    opponents map[string]runtime.Presence
    }

    type Match struct{}

    func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) {
    var debug bool
    if d, ok := params["debug"]; ok {
    if dv, ok := d.(bool); ok {
    debug = dv
    }
    }
    state := &MatchState{
    debug: debug,
    presences: make(map[string]runtime.Presence),
    opponents: make(map[string]runtime.Presence),
    }

    if state.debug {
    logger.Printf("match init, starting with debug: %v", state.debug)
    }

    entries := params["invited"].([]runtime.MatchmakerEntry)
    for i := 0; i < len(entries); i++ {
    p := entries[(i+1)%len(entries)].GetPresence()
    state.opponents[entries[i].GetPresence().GetUsername()] = p
    logger.Info("setting %v 's opponent : %v", entries[i].GetPresence().GetUsername(), entries[(i+1)%len(entries)].GetPresence().GetUsername())
    }

    label := "skill=100-150"
    tickRate := 20 // ticks per second
    return state, tickRate, label
    }

    func (m *Match) MatchJoinAttempt(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, presence runtime.Presence, metadata map[string]string) (interface{}, bool, string) {
    if state.(*MatchState).debug {
    logger.Printf("match join attempt username %v user_id %v session_id %v node %v with metadata %v", presence.GetUsername(), presence.GetUserId(), presence.GetSessionId(), presence.GetNodeId(), metadata)
    }

    return state, true, ""
    }

    func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, presences []runtime.Presence) interface{} {
    mState := state.(*MatchState)
    if mState.debug {
    for _, presence := range presences {
    logger.Printf("match join username %v user_id %v session_id %v node %v", presence.GetUsername(), presence.GetUserId(), presence.GetSessionId(), presence.GetNodeId())
    }
    }

    return mState
    }

    func (m *Match) MatchLeave(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, presences []runtime.Presence) interface{} {
    if state.(*MatchState).debug {
    for _, presence := range presences {
    logger.Printf("match leave username %v user_id %v session_id %v node %v", presence.GetUsername(), presence.GetUserId(), presence.GetSessionId(), presence.GetNodeId())
    }
    }
    return state
    }

    func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
    mState, _ := state.(*MatchState)

    if state.(*MatchState).debug {
    logger.Printf("match loop match_id %v tick %v", ctx.Value(runtime.RUNTIME_CTX_MATCH_ID), tick)
    logger.Printf("match loop match_id %v message count %v", ctx.Value(runtime.RUNTIME_CTX_MATCH_ID), len(messages))
    }

    for _, message := range messages {
    target := mState.opponents[message.GetUsername()]
    dispatcher.BroadcastMessage(message.GetOpCode(), message.GetData(), []runtime.Presence{target}, nil, false)
    }
    return mState
    }

    func (m *Match) MatchTerminate(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, graceSeconds int) interface{} {
    if state.(*MatchState).debug {
    logger.Printf("match terminate match_id %v tick %v", ctx.Value(runtime.RUNTIME_CTX_MATCH_ID), tick)
    logger.Printf("match terminate match_id %v grace seconds %v", ctx.Value(runtime.RUNTIME_CTX_MATCH_ID), graceSeconds)
    }

    return nil
    }