# Authoritative Multiplayer Match Handler Execution Sequence

**URL:** <https://forum.heroiclabs.com/t/authoritative-multiplayer-match-handler-execution-sequence/1505>\
**Category:** Game Design\
**Created:** [March 29, 2021, 1:31pm UTC](https://forum.heroiclabs.com/t/authoritative-multiplayer-match-handler-execution-sequence/1505 "2021-03-29T13:31:10Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mohammed](https://avatars.discourse-cdn.com/v4/letter/m/13edae/32.png) [@Mohammed](https://forum.heroiclabs.com/u/Mohammed)\
**Post date:** [March 29, 2021, 1:31pm UTC](https://forum.heroiclabs.com/t/authoritative-multiplayer-match-handler-execution-sequence/1505/1 "2021-03-29T13:31:10Z")

</div>

I want to understand something about this code snippet  
This code snippet is from match\_handler.go link → [nakama/match\_handler.go at master · heroiclabs/nakama · GitHub](https://github.com/heroiclabs/nakama/blob/master/server/match_handler.go)

```auto
        // Continuously run queued actions until the match stops.
        	go func() {
        		for {
        			select {
        			case <-mh.stopCh:
        				// Match has been stopped.
        				return
        			case <-mh.ticker.C:
        				// Tick, queue a match loop invocation.
        				if !mh.queueCall(loop) {
        					return
        				}
        			case call := <-mh.callCh:
        				// An invocation to one of the match functions, not including join attempts.
        				call(mh)
        			case joinAttempt := <-mh.joinAttemptCh:
        				// An invocation to the join attempt match function.
        				joinAttempt(mh)
        			}
        		}
        	}()

```

From [A Tour of Go](https://tour.golang.org/concurrency/5)`select` means  
A `select` blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

so match\_handler functions will be executed randomly.

what if I have a limit to the number of players in a match, lets say 50  
is it possible that 100 players joined the match same time invoking join attempt match function

```auto
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) {
    mState, _ := state.(*MatchState)
    acceptUser := true
    reasonToReject := ""
	if len(mState.presencesJoined) >=50{
        acceptUser = false
        reasonToReject = "Match is full!"
	}

 
    return state, acceptUser,reasonToReject
}

```

Now because match\_handler functions are randomly executed unluckily MatchJoinAttempt invoked 90 times before MatchJoin is executed once, so we accepted all 90 players before invoking MatchJoin and add them to the presencesJoined

```auto
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)
    for _, p := range presences {
        mState.presences[p.GetUserId()] = p
        presencesJoined = append(presencesJoined, p)

    }
    return mState
}

```

---

<div class="post-metadata">

**Author:** ![zyro](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/zyro/32/14_2.png) [@zyro](https://forum.heroiclabs.com/u/zyro)\
**Post date:** [March 30, 2021, 1:07pm UTC](https://forum.heroiclabs.com/t/authoritative-multiplayer-match-handler-execution-sequence/1505/2 "2021-03-30T13:07:34Z")

</div>

@Mohammed Hi there! Generally you should expect that functions in the match handler might be called in any order the server requires. Do not rely on implementation details in the core server’s scheduling of these functions as these may change.

This means you should absolutely handle the possibility that your match handler will receive many join attempts before the corresponding joins.

Our typical pattern for this is to account for “in-flight” joins, so let’s assume your match state looks like this:

```auto
type MatchState struct {
  presences map[string]runtime.Presence
  joinsInProgress int
}

```

Your join attempt and join pattern might then look like this:

```auto
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) {
  mState, _ := state.(*MatchState)
  if len(mState.presences)+mState.joinsInProgress >= 50 {
    return mState, false, "Match is full!"
  }
  mState.joinsInProgress++
  return mState, 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)
  for _, presence := range presences {
    mState.presences[presenge.GetUserId()] = presence
    mState.joinsInProgress--
  }
  return mState
}

```

This ensures your join attempt handler takes into account both “fully connected” presences and presences that have been accepted but not yet completed their join process. Hope this helps!
