feat(multiplayer): deliver allocated server rosters

This commit is contained in:
Josh Creek
2026-09-01 15:54:07 +01:00
parent 863cf61f1a
commit 68e76b5feb
10 changed files with 267 additions and 22 deletions
+28 -10
View File
@@ -96,6 +96,7 @@ type AssignmentView struct {
}
type AssignmentProvider func(context.Context, string, string, time.Time) (AssignmentView, error)
type RosterProvider func(context.Context, domain.WorkloadBinding, time.Time) ([][]byte, error)
type Service struct {
Sessions *domain.SessionStore
@@ -113,6 +114,7 @@ type Service struct {
ResultSubmitter ResultSubmitter
ServerRegistrar ServerRegistrar
Assignment AssignmentProvider
Roster RosterProvider
Now func() time.Time
Proposals map[string]*domain.Proposal
ProposalBackend ProposalBackend
@@ -461,22 +463,17 @@ type serverRegistrationRequest struct {
}
func (s *Service) serverMutation(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed")
return
}
parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/v1/servers/"), "/")
if len(parts) != 2 || parts[0] == "" || (parts[1] != "result" && parts[1] != "register") {
if len(parts) != 2 || parts[0] == "" || (parts[1] != "result" && parts[1] != "register" && parts[1] != "roster") {
writeError(w, http.StatusNotFound, "not_found")
return
}
if s.WorkloadVerify == nil || (parts[1] == "result" && s.ResultSubmitter == nil) || (parts[1] == "register" && s.ServerRegistrar == nil) {
writeError(w, http.StatusServiceUnavailable, "server_unavailable")
if parts[1] == "roster" && r.Method != http.MethodGet || parts[1] != "roster" && r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed")
return
}
key := r.Header.Get("Idempotency-Key")
if len(key) < 16 || len(key) > 128 {
writeError(w, http.StatusBadRequest, "invalid_idempotency_key")
if s.WorkloadVerify == nil || (parts[1] == "result" && s.ResultSubmitter == nil) || (parts[1] == "register" && s.ServerRegistrar == nil) || (parts[1] == "roster" && s.Roster == nil) {
writeError(w, http.StatusServiceUnavailable, "server_unavailable")
return
}
partsAuth := strings.Fields(r.Header.Get("Authorization"))
@@ -491,6 +488,27 @@ func (s *Service) serverMutation(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusUnauthorized, "unauthorized")
return
}
if parts[1] == "roster" {
roster, err := s.Roster(r.Context(), binding, now)
if err != nil || len(roster) == 0 {
writeError(w, http.StatusUnprocessableEntity, "roster_unavailable")
return
}
encodedRoster := make([]json.RawMessage, 0, len(roster))
for _, envelope := range roster {
encodedRoster = append(encodedRoster, json.RawMessage(envelope))
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(encodedRoster); err != nil {
return
}
return
}
key := r.Header.Get("Idempotency-Key")
if len(key) < 16 || len(key) > 128 {
writeError(w, http.StatusBadRequest, "invalid_idempotency_key")
return
}
if parts[1] == "register" {
var input serverRegistrationRequest
if !decodeBody(w, r, &input) {