|
// 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 |
|
} |