package action import ( "errors" "git.reya.zone/reya/hexmap/server/state" "go.uber.org/zap/zapcore" ) var ( // ErrNoOp is returned when an action has no effect. ErrNoOp = errors.New("action's effects were already applied, or it's an empty action") // ErrNoTransparentColors is returned when a user tries to set their active color or a cell color to transparent. // Transparent here is defined as having an alpha component of less than 15 (0xF). ErrNoTransparentColors = errors.New("transparent colors not allowed") ) // Action is the interface for actions that can be shared between clients, or between the server and a client. type Action interface { zapcore.ObjectMarshaler // Apply causes the action's effects to be applied to s, mutating it in place. // All Actions must conform to the standard that if an action can't be correctly applied, or if it would // have no effect, it returns an error without changing s. // If an action can be correctly applied but would have no effect, it should return ErrNoOp. // If an action is correctly applied and has an effect, it should return nil. Apply(s *state.Synced) error } type Client interface { Server // ToClientPB converts the action into a client action protocol buffer. ToClientPB() *ClientActionPB } type Server interface { Action // ToServerPB converts the action into a server action protocol buffer. ToServerPB() *ServerActionPB } func serverPBFromClient(c Client) *ServerActionPB { return &ServerActionPB{ Action: &ServerActionPB_Client{Client: c.ToClientPB()}, } } type ServerSlice []Server func (s ServerSlice) MarshalLogArray(encoder zapcore.ArrayEncoder) error { var finalErr error = nil for _, a := range s { err := encoder.AppendObject(a) if err != nil && finalErr == nil { finalErr = err } } return finalErr } // CellColor is the action sent when a cell of the map has been colored a different color. type CellColor struct { // At is the location of the cell in storage coordinates. At state.StorageCoordinates `json:"at"` // Color is the color the cell has been changed to. Color state.Color `json:"color"` } func (c CellColor) MarshalLogObject(encoder zapcore.ObjectEncoder) error { encoder.AddString("type", "CellColor") err := encoder.AddObject("at", c.At) encoder.AddString("color", c.Color.String()) return err } // Apply sets the target cell's color, or returns ErrNoOp if it can't. func (c CellColor) Apply(s *state.Synced) error { if c.Color.A < 0xF { return ErrNoTransparentColors } cell, err := s.Map.Layer.GetCellAt(c.At) if err != nil { return err } if cell.Color == c.Color { return ErrNoOp } cell.Color = c.Color return nil } // UserActiveColor is the action sent when the user's current color, the one being painted with, changes. type UserActiveColor struct { // Color is the color that is now active. Color state.Color `json:"color"` } func (c UserActiveColor) MarshalLogObject(encoder zapcore.ObjectEncoder) error { encoder.AddString("type", "UserActiveColor") encoder.AddString("color", c.Color.String()) return nil } // Apply sets the user's active color, or returns ErrNoOp if it can't. func (c UserActiveColor) Apply(s *state.Synced) error { if c.Color.A < 0xF { return ErrNoTransparentColors } if s.User.ActiveColor == c.Color { return ErrNoOp } s.User.ActiveColor = c.Color return nil }