Refactored module structure for ease of testing backend colliders.

This commit is contained in:
genxium 2022-10-14 16:08:22 +08:00
parent 286944b88c
commit e762d257a6
35 changed files with 308 additions and 400 deletions

View File

@ -1,176 +0,0 @@
package main
import (
_ "embed"
"errors"
"fmt"
"image/color"
"time"
"github.com/golang/freetype/truetype"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/solarlune/resolv"
"golang.org/x/image/font"
)
//go:embed excel.ttf
var excelFont []byte
type Game struct {
Worlds []WorldInterface
CurrentWorld int
Width, Height int
Debug bool
ShowHelpText bool
Screen *ebiten.Image
FontFace font.Face
}
func NewGame() *Game {
ebiten.SetWindowResizable(true)
ebiten.SetWindowTitle("resolv test")
g := &Game{
Width: 640,
Height: 360,
ShowHelpText: true,
}
g.Worlds = []WorldInterface{
NewWorldBouncer(g),
NewWorldLineTest(g),
}
fontData, _ := truetype.Parse(excelFont)
g.FontFace = truetype.NewFace(fontData, &truetype.Options{Size: 10})
// Debug FPS rendering
go func() {
for {
fmt.Println("FPS: ", ebiten.CurrentFPS())
fmt.Println("Ticks: ", ebiten.CurrentTPS())
time.Sleep(time.Second)
}
}()
return g
}
func (g *Game) Update() error {
var quit error
if inpututil.IsKeyJustPressed(ebiten.KeyP) {
if ebiten.CurrentTPS() >= 60 {
ebiten.SetMaxTPS(6)
} else {
ebiten.SetMaxTPS(60)
}
}
if inpututil.IsKeyJustPressed(ebiten.KeyF1) {
g.Debug = !g.Debug
}
if inpututil.IsKeyJustPressed(ebiten.KeyF2) {
g.ShowHelpText = !g.ShowHelpText
}
if inpututil.IsKeyJustPressed(ebiten.KeyE) {
g.CurrentWorld++
}
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
g.CurrentWorld--
}
if g.CurrentWorld >= len(g.Worlds) {
g.CurrentWorld = 0
} else if g.CurrentWorld < 0 {
g.CurrentWorld = len(g.Worlds) - 1
}
world := g.Worlds[g.CurrentWorld]
if inpututil.IsKeyJustPressed(ebiten.KeyR) {
world.Init()
}
if ebiten.IsKeyPressed(ebiten.KeyEscape) {
quit = errors.New("quit")
}
world.Update()
return quit
}
func (g *Game) Draw(screen *ebiten.Image) {
g.Screen = screen
screen.Fill(color.RGBA{20, 20, 40, 255})
g.Worlds[g.CurrentWorld].Draw(screen)
}
func (g *Game) DrawText(screen *ebiten.Image, x, y int, textLines ...string) {
rectHeight := 10
for _, txt := range textLines {
w := float64(font.MeasureString(g.FontFace, txt).Round())
ebitenutil.DrawRect(screen, float64(x), float64(y-8), w, float64(rectHeight), color.RGBA{0, 0, 0, 192})
text.Draw(screen, txt, g.FontFace, x+1, y+1, color.RGBA{0, 0, 150, 255})
text.Draw(screen, txt, g.FontFace, x, y, color.RGBA{100, 150, 255, 255})
y += rectHeight
}
}
func (g *Game) DebugDraw(screen *ebiten.Image, space *resolv.Space) {
for y := 0; y < space.Height(); y++ {
for x := 0; x < space.Width(); x++ {
cell := space.Cell(x, y)
cw := float64(space.CellWidth)
ch := float64(space.CellHeight)
cx := float64(cell.X) * cw
cy := float64(cell.Y) * ch
drawColor := color.RGBA{20, 20, 20, 255}
if cell.Occupied() {
drawColor = color.RGBA{255, 255, 0, 255}
}
ebitenutil.DrawLine(screen, cx, cy, cx+cw, cy, drawColor)
ebitenutil.DrawLine(screen, cx+cw, cy, cx+cw, cy+ch, drawColor)
ebitenutil.DrawLine(screen, cx+cw, cy+ch, cx, cy+ch, drawColor)
ebitenutil.DrawLine(screen, cx, cy+ch, cx, cy, drawColor)
}
}
}
func (g *Game) Layout(w, h int) (int, int) {
return g.Width, g.Height
}
func main() {
ebiten.RunGame(NewGame())
}

View File

@ -1,169 +0,0 @@
package main
import (
"fmt"
"image/color"
"math/rand"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/solarlune/resolv"
)
type WorldBouncer struct {
Game *Game
Space *resolv.Space
Geometry []*resolv.Object
Bouncers []*Bouncer
ShowHelpText bool
}
type Bouncer struct {
Object *resolv.Object
SpeedX, SpeedY float64
}
func NewWorldBouncer(game *Game) *WorldBouncer {
w := &WorldBouncer{
Game: game,
ShowHelpText: true,
}
w.Init()
return w
}
func (world *WorldBouncer) Init() {
gw := float64(world.Game.Width)
gh := float64(world.Game.Height)
cellSize := 8
world.Space = resolv.NewSpace(int(gw), int(gh), cellSize, cellSize)
world.Geometry = []*resolv.Object{
resolv.NewObject(0, 0, 16, gh),
resolv.NewObject(gw-16, 0, 16, gh),
resolv.NewObject(0, 0, gw, 16),
resolv.NewObject(0, gh-24, gw, 32),
}
world.Space.Add(world.Geometry...)
world.Bouncers = []*Bouncer{}
world.SpawnObject()
}
func (world *WorldBouncer) SpawnObject() {
bouncer := &Bouncer{
Object: resolv.NewObject(0, 0, 2, 2),
SpeedX: (rand.Float64() * 8) - 4,
SpeedY: (rand.Float64() * 8) - 4,
}
world.Space.Add(bouncer.Object)
// Choose an unoccupied cell to spawn a bouncing object in
var c *resolv.Cell
for c == nil {
rx := rand.Intn(world.Space.Width())
ry := rand.Intn(world.Space.Height())
c = world.Space.Cell(rx, ry)
if c.Occupied() {
c = nil
} else {
bouncer.Object.X, bouncer.Object.Y = world.Space.SpaceToWorld(c.X, c.Y)
}
}
world.Bouncers = append(world.Bouncers, bouncer)
}
func (world *WorldBouncer) Update() {
if ebiten.IsKeyPressed(ebiten.KeyUp) {
for i := 0; i < 5; i++ {
world.SpawnObject()
}
} else if ebiten.IsKeyPressed(ebiten.KeyDown) {
if len(world.Bouncers) > 0 {
b := world.Bouncers[0]
world.Space.Remove(b.Object)
world.Bouncers = world.Bouncers[1:]
}
}
for _, b := range world.Bouncers {
b.SpeedY += 0.1
dx := b.SpeedX
dy := b.SpeedY
if check := b.Object.Check(dx, 0); check != nil {
// We move a bouncer into contact with the owning cell rather than the object because we don't need to be that specific and
// moving into contact with another moving object that bounces away can get them both stuck; it's easier to bounce off of the
// "containing" cells, which are static.
contact := check.ContactWithCell(check.Cells[0])
dx = contact.X()
b.SpeedX *= -1
}
b.Object.X += dx
if check := b.Object.Check(0, dy); check != nil {
contact := check.ContactWithCell(check.Cells[0])
dy = contact.Y()
b.SpeedY *= -1
}
b.Object.Y += dy
b.Object.Update()
}
}
func (world *WorldBouncer) Draw(screen *ebiten.Image) {
for _, o := range world.Geometry {
ebitenutil.DrawRect(screen, o.X, o.Y, o.W, o.H, color.RGBA{60, 60, 60, 255})
}
for _, b := range world.Bouncers {
o := b.Object
ebitenutil.DrawRect(screen, o.X, o.Y, o.W, o.H, color.RGBA{0, 80, 255, 255})
}
if world.Game.Debug {
world.Game.DebugDraw(screen, world.Space)
}
if world.Game.ShowHelpText {
world.Game.DrawText(screen, 16, 16,
"~ Bouncer Demo ~",
"Up Arrow: Add bouncer",
"Down Arrow: Remove bouncer",
"",
fmt.Sprintf("%d Bouncers in the world.", len(world.Bouncers)),
fmt.Sprintf("%d FPS (frames per second)", int(ebiten.CurrentFPS())),
fmt.Sprintf("%d TPS (ticks per second)", int(ebiten.CurrentTPS())),
"",
"F1: Toggle Debug View",
"F2: Show / Hide help text",
"R: Restart world",
"E: Next world",
"Q: Previous world",
)
}
}

View File

@ -2,10 +2,10 @@ package api
import ( import (
"bytes" "bytes"
. "dnmshared"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"io" "io"
"io/ioutil" "io/ioutil"
. "server/common"
) )
func RequestLogger() gin.HandlerFunc { func RequestLogger() gin.HandlerFunc {

View File

@ -16,6 +16,8 @@ import (
"server/models" "server/models"
"server/storage" "server/storage"
"strconv" "strconv"
. "dnmshared"
) )
var Player = playerController{} var Player = playerController{}

View File

@ -1,16 +1,15 @@
package common package common
import ( import (
. "dnmshared"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go.uber.org/zap"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"go.uber.org/zap"
) )
// 隐式导入
var Conf *config var Conf *config
const ( const (
@ -72,11 +71,15 @@ func MustParseConfig() {
BotServer: new(botServerConf), BotServer: new(botServerConf),
} }
execPath, err := os.Executable() execPath, err := os.Executable()
ErrFatal(err) if nil != err {
panic(err)
}
pwd, err := os.Getwd() pwd, err := os.Getwd()
Logger.Debug("os.GetWd", zap.String("pwd", pwd)) Logger.Debug("os.GetWd", zap.String("pwd", pwd))
ErrFatal(err) if nil != err {
panic(err)
}
appRoot := pwd appRoot := pwd
confDir := filepath.Join(appRoot, "configs") confDir := filepath.Join(appRoot, "configs")
@ -129,24 +132,23 @@ func loadJSON(fp string, v interface{}) {
fp = filepath.Join(Conf.General.ConfDir, fp) fp = filepath.Join(Conf.General.ConfDir, fp)
} }
_, err := os.Stat(fp) _, err := os.Stat(fp)
ErrFatal(err) if nil != err {
panic(err)
}
fd, err := os.Open(fp) fd, err := os.Open(fp)
ErrFatal(err) if nil != err {
panic(err)
}
defer fd.Close() defer fd.Close()
Logger.Info("Opened json file successfully.", zap.String("fp", fp)) Logger.Info("Opened json file successfully.", zap.String("fp", fp))
err = json.NewDecoder(fd).Decode(v) err = json.NewDecoder(fd).Decode(v)
ErrFatal(err) if nil != err {
panic(err)
}
Logger.Info("Loaded json file successfully.", zap.String("fp", fp)) Logger.Info("Loaded json file successfully.", zap.String("fp", fp))
} }
// Please only use this auxiliary function before server is fully started up, but not afterwards (启动过程可以使用,运行时不准使用).
func ErrFatal(err error) {
if err != nil {
Logger.Fatal("ErrFatal", zap.NamedError("err", err))
}
}
func isNotExist(p string) bool { func isNotExist(p string) bool {
if _, err := os.Stat(p); err != nil { if _, err := os.Stat(p); err != nil {
return true return true

View File

@ -5,9 +5,10 @@ import (
"github.com/imdario/mergo" "github.com/imdario/mergo"
"go.uber.org/zap" "go.uber.org/zap"
. "dnmshared"
) )
// 隐式导入
var Constants *constants var Constants *constants
func MustParseConstants() { func MustParseConstants() {
@ -24,13 +25,11 @@ func MustParseConstants() {
if !isNotExist(fp) { if !isNotExist(fp) {
testConstants := new(constants) testConstants := new(constants)
loadJSON(fp, testConstants) loadJSON(fp, testConstants)
//Logger.Debug(spew.Sdump(Constants))
//Logger.Debug(spew.Sdump(testConstants))
err := mergo.Merge(testConstants, Constants) err := mergo.Merge(testConstants, Constants)
ErrFatal(err) if nil != err {
panic(err)
}
Constants = testConstants Constants = testConstants
//Logger.Debug("mergo.Merge", zap.Error(err))
//Logger.Debug(spew.Sdump(testConstants))
} }
} }
constantsPost() constantsPost()

View File

@ -3,6 +3,7 @@ package utils
import ( import (
"bytes" "bytes"
"crypto/sha1" "crypto/sha1"
. "dnmshared"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"

View File

@ -11,7 +11,6 @@ var (
RE_CHINA_PHONE_NUM = regexp.MustCompile(`^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$`) RE_CHINA_PHONE_NUM = regexp.MustCompile(`^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$`)
) )
// 隐式导入
var ConstVals = &struct { var ConstVals = &struct {
Player struct { Player struct {
CaptchaExpire time.Duration CaptchaExpire time.Duration

View File

@ -1,6 +1,7 @@
package env_tools package env_tools
import ( import (
. "dnmshared"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@ -15,7 +16,9 @@ func LoadPreConf() {
Logger.Info(`Merging PreConfSQLite data into MySQL`, Logger.Info(`Merging PreConfSQLite data into MySQL`,
zap.String("PreConfSQLitePath", Conf.General.PreConfSQLitePath)) zap.String("PreConfSQLitePath", Conf.General.PreConfSQLitePath))
db, err := sqlx.Connect("sqlite3", Conf.General.PreConfSQLitePath) db, err := sqlx.Connect("sqlite3", Conf.General.PreConfSQLitePath)
ErrFatal(err) if nil != err {
panic(err)
}
defer db.Close() defer db.Close()
loadPreConfToMysql(db) loadPreConfToMysql(db)
@ -39,7 +42,9 @@ func loadPreConfToMysql(db *sqlx.DB) {
func loadSqlite(db *sqlx.DB, tbs []string) { func loadSqlite(db *sqlx.DB, tbs []string) {
for _, v := range tbs { for _, v := range tbs {
result, err := storage.MySQLManagerIns.Exec("truncate " + v) result, err := storage.MySQLManagerIns.Exec("truncate " + v)
ErrFatal(err) if nil != err {
panic(err)
}
Logger.Info("truncate", zap.Any("truncate "+v, result)) Logger.Info("truncate", zap.Any("truncate "+v, result))
query, args, err := sq.Select("*").From(v).ToSql() query, args, err := sq.Select("*").From(v).ToSql()
if err != nil { if err != nil {
@ -70,19 +75,25 @@ func createMysqlData(rows *sqlx.Rows, v string) {
func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) { func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
var ls []*dbBotPlayer var ls []*dbBotPlayer
err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num, display_name FROM "+tableName) err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num, display_name FROM "+tableName)
ErrFatal(err) if nil != err {
panic(err)
}
names := make([]string, len(ls), len(ls)) names := make([]string, len(ls), len(ls))
for i, v := range ls { for i, v := range ls {
names[i] = v.Name names[i] = v.Name
} }
sql := "SELECT name FROM `player` WHERE name in (?)" sql := "SELECT name FROM `player` WHERE name in (?)"
query, args, err := sqlx.In(sql, names) query, args, err := sqlx.In(sql, names)
ErrFatal(err) if nil != err {
panic(err)
}
query = storage.MySQLManagerIns.Rebind(query) query = storage.MySQLManagerIns.Rebind(query)
// existNames := make([]string, len(ls), len(ls)) // existNames := make([]string, len(ls), len(ls))
var existPlayers []*models.Player var existPlayers []*models.Player
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...) err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
ErrFatal(err) if nil != err {
panic(err)
}
for _, botPlayer := range ls { for _, botPlayer := range ls {
var flag bool var flag bool

View File

@ -1,6 +1,7 @@
package env_tools package env_tools
import ( import (
. "dnmshared"
. "server/common" . "server/common"
"server/common/utils" "server/common/utils"
"server/models" "server/models"
@ -15,7 +16,9 @@ func MergeTestPlayerAccounts() {
fp := Conf.General.TestEnvSQLitePath fp := Conf.General.TestEnvSQLitePath
Logger.Info(`Initializing TestPlayerAccounts in runtime MySQLServer from SQLite file:`, zap.String("fp", fp)) Logger.Info(`Initializing TestPlayerAccounts in runtime MySQLServer from SQLite file:`, zap.String("fp", fp))
db, err := sqlx.Connect("sqlite3", fp) db, err := sqlx.Connect("sqlite3", fp)
ErrFatal(err) if nil != err {
panic(err)
}
defer db.Close() defer db.Close()
maybeCreateNewPlayer(db, "test_player") maybeCreateNewPlayer(db, "test_player")
} }
@ -29,31 +32,35 @@ type dbTestPlayer struct {
func maybeCreateNewPlayer(db *sqlx.DB, tableName string) { func maybeCreateNewPlayer(db *sqlx.DB, tableName string) {
var ls []*dbTestPlayer var ls []*dbTestPlayer
err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num FROM "+tableName) err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num FROM "+tableName)
ErrFatal(err) if nil != err {
panic(err)
}
names := make([]string, len(ls), len(ls)) names := make([]string, len(ls), len(ls))
for i, v := range ls { for i, v := range ls {
names[i] = v.Name names[i] = v.Name
} }
sql := "SELECT name FROM `player` WHERE name in (?)" sql := "SELECT name FROM `player` WHERE name in (?)"
query, args, err := sqlx.In(sql, names) query, args, err := sqlx.In(sql, names)
ErrFatal(err) if nil != err {
panic(err)
}
query = storage.MySQLManagerIns.Rebind(query) query = storage.MySQLManagerIns.Rebind(query)
// existNames := make([]string, len(ls), len(ls)) // existNames := make([]string, len(ls), len(ls))
var existPlayers []*models.Player var existPlayers []*models.Player
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...) err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
ErrFatal(err) if nil != err {
panic(err)
}
for _, testPlayer := range ls { for _, testPlayer := range ls {
var flag bool var flag bool
for _, v := range existPlayers { for _, v := range existPlayers {
if testPlayer.Name == v.Name { if testPlayer.Name == v.Name {
// 已有数据,合并处理
flag = true flag = true
break break
} }
} }
if !flag { if !flag {
// 找不到,新增
Logger.Debug("create", zap.Any(tableName, testPlayer)) Logger.Debug("create", zap.Any(tableName, testPlayer))
err := createNewPlayer(testPlayer) err := createNewPlayer(testPlayer)
if err != nil { if err != nil {

View File

@ -20,6 +20,8 @@ require (
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713 github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713
go.uber.org/zap v1.9.1 go.uber.org/zap v1.9.1
google.golang.org/protobuf v1.28.1 google.golang.org/protobuf v1.28.1
dnmshared v0.0.0
) )
require ( require (
@ -43,3 +45,7 @@ require (
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.2.1 // indirect
) )
replace (
dnmshared => ../dnmshared
)

View File

@ -17,6 +17,8 @@ import (
"syscall" "syscall"
"time" "time"
. "dnmshared"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/robfig/cron" "github.com/robfig/cron"

View File

@ -1,5 +1,9 @@
package models package models
import (
. "dnmshared"
)
type Barrier struct { type Barrier struct {
Boundary *Polygon2D Boundary *Polygon2D
} }

View File

@ -1,6 +1,7 @@
package models package models
import ( import (
. "dnmshared"
pb "server/pb_output" pb "server/pb_output"
) )

View File

@ -2,6 +2,7 @@ package models
import ( import (
"database/sql" "database/sql"
. "dnmshared"
"fmt" "fmt"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"

View File

@ -2,7 +2,7 @@ package models
import ( import (
"database/sql" "database/sql"
. "server/common" . "dnmshared"
"server/storage" "server/storage"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"

View File

@ -2,6 +2,7 @@ package models
import ( import (
"database/sql" "database/sql"
. "dnmshared"
"errors" "errors"
. "server/common" . "server/common"
"server/common/utils" "server/common/utils"

View File

@ -1,17 +1,14 @@
package models package models
import ( import (
"encoding/xml" . "dnmshared"
"fmt" "fmt"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/solarlune/resolv" "github.com/solarlune/resolv"
"go.uber.org/zap" "go.uber.org/zap"
"io/ioutil"
"math" "math"
"math/rand" "math/rand"
"os"
"path/filepath"
. "server/common" . "server/common"
"server/common/utils" "server/common/utils"
pb "server/pb_output" pb "server/pb_output"
@ -19,6 +16,11 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"encoding/xml"
"io/ioutil"
"os"
"path/filepath"
) )
const ( const (
@ -261,7 +263,9 @@ func (pR *Room) ChooseStage() error {
* -- YFLu, 2019-09-04 * -- YFLu, 2019-09-04
*/ */
pwd, err := os.Getwd() pwd, err := os.Getwd()
ErrFatal(err) if nil != err {
panic(err)
}
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
stageNameList := []string{ /*"pacman" ,*/ "richsoil"} stageNameList := []string{ /*"pacman" ,*/ "richsoil"}

View File

@ -2,9 +2,9 @@ package models
import ( import (
"container/heap" "container/heap"
. "dnmshared"
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"
. "server/common"
"sync" "sync"
) )

View File

@ -6,6 +6,8 @@ import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"go.uber.org/zap" "go.uber.org/zap"
. "dnmshared"
) )
var ( var (
@ -15,8 +17,12 @@ var (
func initMySQL() { func initMySQL() {
var err error var err error
MySQLManagerIns, err = sqlx.Connect("mysql", Conf.MySQL.DSN+"?charset=utf8mb4") MySQLManagerIns, err = sqlx.Connect("mysql", Conf.MySQL.DSN+"?charset=utf8mb4")
ErrFatal(err) if nil != err {
panic(err)
}
err = MySQLManagerIns.Ping() err = MySQLManagerIns.Ping()
ErrFatal(err) if nil != err {
panic(err)
}
Logger.Info("MySQLManagerIns", zap.Any("mysql", MySQLManagerIns)) Logger.Info("MySQLManagerIns", zap.Any("mysql", MySQLManagerIns))
} }

View File

@ -7,6 +7,8 @@ import (
"github.com/go-redis/redis" "github.com/go-redis/redis"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"go.uber.org/zap" "go.uber.org/zap"
. "dnmshared"
) )
var ( var (
@ -20,6 +22,8 @@ func initRedis() {
DB: Conf.Redis.Dbname, // use default DB DB: Conf.Redis.Dbname, // use default DB
}) })
pong, err := RedisManagerIns.Ping().Result() pong, err := RedisManagerIns.Ping().Result()
ErrFatal(err) if nil != err {
panic(err)
}
Logger.Info("Redis", zap.String("ping", pong)) Logger.Info("Redis", zap.String("ping", pong))
} }

View File

@ -17,7 +17,9 @@ func loadTMX(fp string, pTmxMapIns *models.TmxMap) {
} }
byteArr, err := ioutil.ReadFile(fp) byteArr, err := ioutil.ReadFile(fp)
ErrFatal(err) if nil != err {
panic(err)
}
models.DeserializeToTmxMapIns(byteArr, pTmxMapIns) models.DeserializeToTmxMapIns(byteArr, pTmxMapIns)
for _, info := range pTmxMapIns.TreasuresInfo { for _, info := range pTmxMapIns.TreasuresInfo {
fmt.Printf("treasuresInfo: %v\n", info) fmt.Printf("treasuresInfo: %v\n", info)
@ -33,7 +35,9 @@ func loadTSX(fp string, pTsxIns *models.Tsx) {
} }
byteArr, err := ioutil.ReadFile(fp) byteArr, err := ioutil.ReadFile(fp)
ErrFatal(err) if nil != err {
panic(err)
}
models.DeserializeToTsxIns(byteArr, pTsxIns) models.DeserializeToTsxIns(byteArr, pTsxIns)
for _, Pos := range pTsxIns.TrapPolyLineList { for _, Pos := range pTsxIns.TrapPolyLineList {
fmt.Printf("%v\n", Pos) fmt.Printf("%v\n", Pos)
@ -43,10 +47,14 @@ func loadTSX(fp string, pTsxIns *models.Tsx) {
func getTMXInfo() { func getTMXInfo() {
relativePath = "../frontend/assets/resources/map/treasurehunter.tmx" relativePath = "../frontend/assets/resources/map/treasurehunter.tmx"
execPath, err := os.Executable() execPath, err := os.Executable()
ErrFatal(err) if nil != err {
panic(err)
}
pwd, err := os.Getwd() pwd, err := os.Getwd()
ErrFatal(err) if nil != err {
panic(err)
}
fmt.Printf("execPath = %v, pwd = %s, returning...\n", execPath, pwd) fmt.Printf("execPath = %v, pwd = %s, returning...\n", execPath, pwd)
@ -61,10 +69,14 @@ func getTSXInfo() {
relativePath = "../frontend/assets/resources/map/tile_1.tsx" relativePath = "../frontend/assets/resources/map/tile_1.tsx"
execPath, err := os.Executable() execPath, err := os.Executable()
ErrFatal(err) if nil != err {
panic(err)
}
pwd, err := os.Getwd() pwd, err := os.Getwd()
ErrFatal(err) if nil != err {
panic(err)
}
fmt.Printf("execPath = %v, pwd = %s, returning...\n", execPath, pwd) fmt.Printf("execPath = %v, pwd = %s, returning...\n", execPath, pwd)
tsxIns := models.Tsx{} tsxIns := models.Tsx{}

View File

@ -14,6 +14,8 @@ import (
"strconv" "strconv"
"sync/atomic" "sync/atomic"
"time" "time"
. "dnmshared"
) )
const ( const (

View File

@ -7,6 +7,7 @@ require (
github.com/hajimehoshi/ebiten/v2 v2.4.7 github.com/hajimehoshi/ebiten/v2 v2.4.7
github.com/solarlune/resolv v0.5.1 github.com/solarlune/resolv v0.5.1
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
dnmshared v0.0.0
) )
require ( require (
@ -15,7 +16,12 @@ require (
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 // indirect github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 // indirect
github.com/jezek/xgb v1.0.1 // indirect github.com/jezek/xgb v1.0.1 // indirect
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 // indirect github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
) )
replace dnmshared => ../dnmshared

View File

@ -1,4 +1,6 @@
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 h1:A8UnJ/5OKzki4HBDwoRQz7I6sxKsokpMXcGh+fUxpfc= github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 h1:A8UnJ/5OKzki4HBDwoRQz7I6sxKsokpMXcGh+fUxpfc=
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg= github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad h1:kX51IjbsJPCvzV9jUoVQG9GEUqIq5hjfYzXTqQ52Rh8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad h1:kX51IjbsJPCvzV9jUoVQG9GEUqIq5hjfYzXTqQ52Rh8=
@ -22,10 +24,19 @@ github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3T
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 h1:v8lWpj5957KtDMKu+xQtlu6G3ZoZR6Tn9bsfZCRG5Xw= github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 h1:v8lWpj5957KtDMKu+xQtlu6G3ZoZR6Tn9bsfZCRG5Xw=
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0/go.mod h1:GAX7tMJqXx9fB1BrsTWPOXy6IBRX+J461BffVPAdpwo= github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0/go.mod h1:GAX7tMJqXx9fB1BrsTWPOXy6IBRX+J461BffVPAdpwo=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/solarlune/resolv v0.5.1 h1:Ul6PAs/zaxiMUOEYz1Z6VeUj5k3CDcWMvSh+kivybDY= github.com/solarlune/resolv v0.5.1 h1:Ul6PAs/zaxiMUOEYz1Z6VeUj5k3CDcWMvSh+kivybDY=
github.com/solarlune/resolv v0.5.1/go.mod h1:HjM2f/0NoVjVdZsi26GtugX5aFbA62COEFEXkOhveRw= github.com/solarlune/resolv v0.5.1/go.mod h1:HjM2f/0NoVjVdZsi26GtugX5aFbA62COEFEXkOhveRw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

168
collider_visualizer/main.go Normal file
View File

@ -0,0 +1,168 @@
package main
import (
_ "embed"
"fmt"
"image/color"
"go.uber.org/zap"
"github.com/golang/freetype/truetype"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/solarlune/resolv"
"golang.org/x/image/font"
"encoding/xml"
"io/ioutil"
"os"
"path/filepath"
. "dnmshared"
)
func parseStage(stageName string) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) {
pwd, err := os.Getwd()
if nil != err {
Logger.Error("Failed to get current working dir:", zap.Any("pwd", pwd), zap.Any("err", err))
}
relativePathForAllStages := "../frontend/assets/resources/map"
relativePathForChosenStage := fmt.Sprintf("%s/%s", relativePathForAllStages, stageName)
pTmxMapIns := &TmxMap{}
absDirPathContainingDirectlyTmxFile := filepath.Join(pwd, relativePathForChosenStage)
absTmxFilePath := fmt.Sprintf("%s/map.tmx", absDirPathContainingDirectlyTmxFile)
if !filepath.IsAbs(absTmxFilePath) {
panic("Tmx filepath must be absolute!")
}
byteArr, err := ioutil.ReadFile(absTmxFilePath)
if nil != err {
panic(err)
}
err = xml.Unmarshal(byteArr, pTmxMapIns)
if nil != err {
panic(err)
}
// Obtain the content of `gidBoundariesMapInB2World`.
gidBoundariesMapInB2World := make(map[int]StrToPolygon2DListMap, 0)
for _, tileset := range pTmxMapIns.Tilesets {
relativeTsxFilePath := fmt.Sprintf("%s/%s", filepath.Join(pwd, relativePathForChosenStage), tileset.Source) // Note that "TmxTileset.Source" can be a string of "relative path".
absTsxFilePath, err := filepath.Abs(relativeTsxFilePath)
if nil != err {
panic(err)
}
if !filepath.IsAbs(absTsxFilePath) {
panic("Filepath must be absolute!")
}
byteArrOfTsxFile, err := ioutil.ReadFile(absTsxFilePath)
if nil != err {
panic(err)
}
DeserializeTsxToColliderDict(pTmxMapIns, byteArrOfTsxFile, int(tileset.FirstGid), gidBoundariesMapInB2World)
}
return ParseTmxLayersAndGroups(pTmxMapIns, gidBoundariesMapInB2World)
}
//go:embed excel.ttf
var excelFont []byte
type Game struct {
World WorldInterface
Width, Height int
Debug bool
ShowHelpText bool
Screen *ebiten.Image
FontFace font.Face
}
func NewGame() *Game {
ebiten.SetWindowResizable(true)
ebiten.SetWindowTitle("resolv test")
g := &Game{
Width: 640,
Height: 360,
ShowHelpText: true,
}
g.World = NewWorldLineTest(g)
fontData, _ := truetype.Parse(excelFont)
g.FontFace = truetype.NewFace(fontData, &truetype.Options{Size: 10})
return g
}
func (g *Game) Update() error {
g.World.Update()
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
g.Screen = screen
screen.Fill(color.RGBA{20, 20, 40, 255})
g.World.Draw(screen)
}
func (g *Game) DrawText(screen *ebiten.Image, x, y int, textLines ...string) {
rectHeight := 10
for _, txt := range textLines {
w := float64(font.MeasureString(g.FontFace, txt).Round())
ebitenutil.DrawRect(screen, float64(x), float64(y-8), w, float64(rectHeight), color.RGBA{0, 0, 0, 192})
text.Draw(screen, txt, g.FontFace, x+1, y+1, color.RGBA{0, 0, 150, 255})
text.Draw(screen, txt, g.FontFace, x, y, color.RGBA{100, 150, 255, 255})
y += rectHeight
}
}
func (g *Game) DebugDraw(screen *ebiten.Image, space *resolv.Space) {
for y := 0; y < space.Height(); y++ {
for x := 0; x < space.Width(); x++ {
cell := space.Cell(x, y)
cw := float64(space.CellWidth)
ch := float64(space.CellHeight)
cx := float64(cell.X) * cw
cy := float64(cell.Y) * ch
drawColor := color.RGBA{20, 20, 20, 255}
if cell.Occupied() {
drawColor = color.RGBA{255, 255, 0, 255}
}
ebitenutil.DrawLine(screen, cx, cy, cx+cw, cy, drawColor)
ebitenutil.DrawLine(screen, cx+cw, cy, cx+cw, cy+ch, drawColor)
ebitenutil.DrawLine(screen, cx+cw, cy+ch, cx, cy+ch, drawColor)
ebitenutil.DrawLine(screen, cx, cy+ch, cx, cy, drawColor)
}
}
}
func (g *Game) Layout(w, h int) (int, int) {
return g.Width, g.Height
}
func main() {
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, toRetStrToVec2DListMap, toRetStrToPolygon2DListMap, err := parseStage("richsoil")
if nil != err {
panic(err)
}
Logger.Info("Parsed variables", zap.Any("stageDiscreteW", stageDiscreteW), zap.Any("stageDiscreteH", stageDiscreteH), zap.Any("stageTileW", stageTileW), zap.Any("stageTileH", stageTileH), zap.Any("toRetStrToVec2DListMap", toRetStrToVec2DListMap), zap.Any("toRetStrToPolygon2DListMap", toRetStrToPolygon2DListMap))
ebiten.RunGame(NewGame())
}

View File

@ -1,4 +1,4 @@
package models package dnmshared
import ( import (
"math" "math"

3
dnmshared/go.mod Normal file
View File

@ -0,0 +1,3 @@
module tiled
go 1.19

View File

@ -1,4 +1,4 @@
package common package dnmshared
import ( import (
"go.uber.org/zap" "go.uber.org/zap"
@ -19,5 +19,7 @@ func init() {
LoggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder LoggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
var err error var err error
Logger, err = LoggerConfig.Build() Logger, err = LoggerConfig.Build()
ErrFatal(err) if nil != err {
panic(err)
}
} }

View File

@ -1,4 +1,4 @@
package models package dnmshared
import ( import (
"bytes" "bytes"
@ -9,7 +9,6 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"io/ioutil" "io/ioutil"
"math" "math"
. "server/common"
"strconv" "strconv"
"strings" "strings"
) )