adding validated services? patching forcad_local.py

This commit is contained in:
Your Name
2026-08-13 08:32:35 +07:00
parent d485f61169
commit e795614e45
1459 changed files with 420036 additions and 436 deletions

View File

@@ -0,0 +1,176 @@
package game
import (
"context"
"encoding/json"
"fmt"
"log"
"omctf.ru/block-game-backend/db"
"omctf.ru/block-game-backend/messaging"
"omctf.ru/block-game-backend/utils"
"omctf.ru/block-game-backend/utils/xy"
)
type Session struct {
Conn *messaging.Conn
UserId int
LevelId int
Tiles Tiles
}
func (sess *Session) Start() {
sess.Conn.Start()
go sess.LoopReceive()
}
func (sess *Session) LoopReceive() {
defer sess.Conn.Close()
for {
msg, ok := <-sess.Conn.Recv
if !ok {
return
}
err := sess.processMessage(msg)
if _, ok := err.(messaging.ErrorBadRequest); ok {
sess.Conn.Error(err)
} else if err != nil {
sess.Conn.Error(fmt.Errorf("internal server error"))
return
}
}
}
type Move struct {
Direction xy.Direction `json:"direction"`
}
type Update struct {
Idx int `json:"idx"`
NewTile Tile `json:"new_tile"`
}
func (sess *Session) MoveTile(tile *Tile, newPos xy.Point) {
idx := sess.Tiles.Index(tile)
if idx == -1 {
log.Panicf("tile not found in session")
}
sess.MoveTileAt(idx, newPos)
}
func (sess *Session) MoveTileAt(tileIdx int, newPos xy.Point) {
tile := &sess.Tiles.Tiles[tileIdx]
tile.Pos = newPos
sess.Conn.Send(messaging.Message{
Type: "update",
Option: utils.MustMarshal(map[string]any{
"idx": tileIdx,
"new_tile": tile,
}),
})
}
func unpackMessageData(msg messaging.Message) (any, error) {
switch msg.Type {
case "move":
var move Move
err := json.Unmarshal(msg.Option, &move)
if err != nil {
return nil, messaging.ErrorBadRequestf("invalid option: %w", err)
}
if err := move.Direction.Validate(); err != nil {
return nil, messaging.ErrorBadRequestf("invalid direction: %w", err)
}
return move, nil
}
return nil, messaging.ErrorBadRequestf("unknown message type")
}
func (sess *Session) processMessage(message messaging.Message) error {
data, err := unpackMessageData(message)
if err != nil {
return err
}
switch data := data.(type) {
case Move:
player, err := sess.Tiles.GetPlayerTile()
if err != nil || player == nil {
return messaging.ErrorBadRequestf("can't get player tile: %w", err)
}
newPos, err := player.Pos.Go(data.Direction)
if err != nil {
return err
}
if !sess.MoveOutOfWay(newPos, data.Direction) {
return nil
}
sess.MoveTile(player, newPos)
if sess.Tiles.At(newPos).Has("exit") {
level, err := db.Client.Level.Get(context.Background(), sess.LevelId)
var prize string
if level == nil || err != nil {
prize = "can't get level prize"
} else {
prize = level.Prize
}
sess.Conn.Send(messaging.Message{
Type: "level_complete",
Option: utils.MustMarshal(map[string]any{
"prize": prize,
}),
})
}
default:
return messaging.ErrorBadRequestf("unknown message type")
}
return nil
}
type DoorData struct {
ButtonPos xy.Point `json:"button_position"`
}
func (sess *Session) isDoorOpen(door *Tile) bool {
if door.Data == nil {
return false
}
var doorData DoorData
err := json.Unmarshal(door.Data, &doorData)
if err != nil {
return false
}
tiles := sess.Tiles.At(doorData.ButtonPos)
return len(tiles) > 0
}
func (sess *Session) MoveOutOfWay(p xy.Point, dir xy.Direction) bool {
tiles := sess.Tiles.At(p)
wall, err := tiles.GetTheOnly("wall")
if err != nil || wall != nil {
return false
}
door, err := tiles.GetTheOnly("door")
if err != nil {
return false
}
if door != nil && !sess.isDoorOpen(door) {
return false
}
box, err := tiles.GetTheOnly("box")
if err != nil {
return false
}
if box == nil {
return true
}
newPos, err := box.Pos.Go(dir)
if err != nil {
return false
}
if sess.MoveOutOfWay(newPos, dir) {
sess.MoveTile(box, newPos)
return true
} else {
return false
}
}