Skip to content

Commit

Permalink
Adding in context checking for playerID, method to read any byte arra…
Browse files Browse the repository at this point in the history
…y to an event
  • Loading branch information
murphysean committed Apr 8, 2018
1 parent e516f8b commit 2e3bc3f
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 6 deletions.
83 changes: 83 additions & 0 deletions events.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package sh

import (
"encoding/json"
"errors"
"time"
)

Expand Down Expand Up @@ -30,6 +32,87 @@ type Event interface {
GetType() string
}

func UnmarshalEvent(b []byte) (Event, error) {
//read it as a base event
bt := BaseEvent{}
err := json.Unmarshal(b, &bt)
if err != nil {
return nil, err
}
//finally read it as it's real event
switch bt.GetType() {
case TypePlayerJoin:
fallthrough
case TypePlayerReady:
fallthrough
case TypePlayerAcknowledge:
e := PlayerEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
case TypePlayerNominate:
fallthrough
case TypePlayerSpecialElection:
fallthrough
case TypePlayerExecute:
fallthrough
case TypePlayerInvestigate:
e := PlayerPlayerEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
case TypePlayerVote:
e := PlayerVoteEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
case TypePlayerLegislate:
e := PlayerLegislateEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
case TypeRequestAcknowledge:
fallthrough
case TypeRequestVote:
fallthrough
case TypeRequestNominate:
fallthrough
case TypeRequestLegislate:
fallthrough
case TypeRequestExecutiveAction:
e := RequestEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
case TypeGameInformation:
e := InformationEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
case TypeGameUpdate:
e := GameEvent{}
err = json.Unmarshal(b, &e)
if err != nil {
return bt, err
}
return e, nil
default:
return bt, errors.New("Unknown Event Type")
}
}

type BaseEvent struct {
ID int `json:"id"`
Type string `json:"type"`
Expand Down
4 changes: 4 additions & 0 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func (g Game) Filter(ctx context.Context) Game {
InvestigatedBy: p.InvestigatedBy,
ExecutedBy: p.ExecutedBy,
}
if me.ID == p.ID {
np.Party = p.Party
np.Role = p.Role
}
if me.ID == p.InvestigatedBy {
np.Party = p.Party
}
Expand Down
51 changes: 45 additions & 6 deletions game.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"io"
"sync"
)

Expand Down Expand Up @@ -45,35 +45,50 @@ func NewSecretHitler() *SecretHitler {
ret := new(SecretHitler)
ret.subscribers = make(map[string]chan<- Event)
ec := make(chan Event)
//Make the engine a subscriber
ret.subscribers["engine"] = ec
go func() {
for {
select {
case e := <-ec:
//TODO If the game is over, then return
if e == nil {
fmt.Println("Exiting game engine loop via nil read")
return
}
if nes, err := ret.Engine(e); err == nil {
fmt.Println("engine: Produced:", nes)
for _, ne := range nes {
err = ret.SubmitEvent(context.TODO(), ne)
ctx := context.Background()
ctx = context.WithValue(ctx, "playerID", "engine")
err = ret.SubmitEvent(ctx, ne)
if err != nil {
fmt.Println("Apply Error:", err)
}
}
}
//If the game is over, then return
if ret.Game.State == GameStateFinished {
ret.m.Lock()
for k, v := range ret.subscribers {
delete(ret.subscribers, k)
close(v)
}
ret.m.Unlock()
break
}
}
}
fmt.Println("Exiting game engine loop")
fmt.Println("Exiting game engine loop via loop break")
}()
return ret
}

type SecretHitler struct {
Game

Log *os.File
Log io.Writer
m sync.RWMutex

//Make the engine a subscriber
subscribers map[string]chan<- Event
}

Expand Down Expand Up @@ -105,6 +120,9 @@ func (sh *SecretHitler) SubmitEvent(ctx context.Context, e Event) error {
}

func (sh *SecretHitler) AddSubscriber(key string, channel chan<- Event) {
if sh.Game.State == GameStateFinished {
return
}
sh.m.Lock()
sh.subscribers[key] = channel
sh.m.Unlock()
Expand All @@ -124,6 +142,27 @@ func (sh *SecretHitler) BroadcastEvent(e Event) {
}
}

//ReadEventLog will read the associated event log and publish all the events to the included channel
func ReadEventLog(r io.Reader, c chan<- Event) error {
//Read in a byte slice
d := json.NewDecoder(r)
var err error
for err == nil {
var rm json.RawMessage
err = d.Decode(&rm)
if err != nil {
return err
}
e, err := UnmarshalEvent(rm)
if err != nil {
return err
}
c <- e
}
close(c)
return nil
}

type Game struct {
EventID int `json:"eventID,omitempty"`
State string `json:"state,omitempty"`
Expand Down
32 changes: 32 additions & 0 deletions validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import (
//Validate ensures that an event is consistent with the current state and then
//sends it to the event log.
func (g Game) Validate(ctx context.Context, e Event) error {
pid := ctx.Value("playerID").(string)
//Players must all be ready for game to start
switch e.GetType() {
case TypePlayerJoin:
pje := e.(PlayerEvent)
if pje.Player.ID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.State != GameStateLobby {
return errors.New("Players can only join while the game is in the lobby state")
}
Expand All @@ -25,6 +29,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
case TypePlayerReady:
pre := e.(PlayerEvent)
if pre.Player.ID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.State != GameStateLobby {
return errors.New("Players can only ready while the game is in the lobby state")
}
Expand All @@ -40,6 +47,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
return errors.New("No player found with matching ID")
case TypePlayerAcknowledge:
pae := e.(PlayerEvent)
if pae.Player.ID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.State != "init" {
return errors.New("Players can only ack while the game is in the init state")
}
Expand All @@ -63,6 +73,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
return errors.New("No player found with matching ID")
case TypePlayerNominate:
ope := e.(PlayerPlayerEvent)
if ope.PlayerID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.Round.State != RoundStateNominating {
return errors.New("Players can only vote while the round is in the nominating state")
}
Expand All @@ -84,6 +97,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
case TypePlayerVote:
pve := e.(PlayerVoteEvent)
if pve.PlayerID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.Round.State != RoundStateVoting {
return errors.New("Players can only vote while the round is in the voting state")
}
Expand All @@ -106,6 +122,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
case TypePlayerLegislate:
ple := e.(PlayerLegislateEvent)
if ple.PlayerID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.Round.State != RoundStateLegislating {
return errors.New("Players can only legislate while the round is in the legislating state")
}
Expand All @@ -132,6 +151,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
case TypePlayerInvestigate:
ope := e.(PlayerPlayerEvent)
if ope.PlayerID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.Round.State != RoundStateExecutiveAction {
return errors.New("Players can only investigate while the round is in the executive_action state")
}
Expand All @@ -150,6 +172,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
case TypePlayerSpecialElection:
ope := e.(PlayerPlayerEvent)
if ope.PlayerID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.Round.State != RoundStateExecutiveAction {
return errors.New("Players can only call a special election while the round is in the executive_action state")
}
Expand All @@ -168,6 +193,9 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
case TypePlayerExecute:
ope := e.(PlayerPlayerEvent)
if ope.PlayerID != pid {
return errors.New("PlayerID must match currently authenticated user")
}
if g.Round.State != RoundStateExecutiveAction {
return errors.New("Players can only execute while the round is in the executive_action state")
}
Expand All @@ -184,6 +212,10 @@ func (g Game) Validate(ctx context.Context, e Event) error {
}
}
}
default:
if pid != "admin" || pid != "engine" {
return errors.New("Not Authorized")
}
}

return nil
Expand Down

0 comments on commit 2e3bc3f

Please sign in to comment.