mirror of
https://github.com/genxium/DelayNoMore
synced 2026-02-12 11:22:50 +00:00
Refactored module structure for ease of testing backend colliders.
This commit is contained in:
@@ -2,10 +2,10 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
. "dnmshared"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
. "server/common"
|
||||
)
|
||||
|
||||
func RequestLogger() gin.HandlerFunc {
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"server/models"
|
||||
"server/storage"
|
||||
"strconv"
|
||||
|
||||
. "dnmshared"
|
||||
)
|
||||
|
||||
var Player = playerController{}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
. "dnmshared"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 隐式导入
|
||||
var Conf *config
|
||||
|
||||
const (
|
||||
@@ -72,11 +71,15 @@ func MustParseConfig() {
|
||||
BotServer: new(botServerConf),
|
||||
}
|
||||
execPath, err := os.Executable()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
Logger.Debug("os.GetWd", zap.String("pwd", pwd))
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
appRoot := pwd
|
||||
confDir := filepath.Join(appRoot, "configs")
|
||||
@@ -129,22 +132,21 @@ func loadJSON(fp string, v interface{}) {
|
||||
fp = filepath.Join(Conf.General.ConfDir, fp)
|
||||
}
|
||||
_, err := os.Stat(fp)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fd, err := os.Open(fp)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
defer fd.Close()
|
||||
Logger.Info("Opened json file successfully.", zap.String("fp", fp))
|
||||
err = json.NewDecoder(fd).Decode(v)
|
||||
ErrFatal(err)
|
||||
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))
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
Logger.Info("Loaded json file successfully.", zap.String("fp", fp))
|
||||
}
|
||||
|
||||
func isNotExist(p string) bool {
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"go.uber.org/zap"
|
||||
|
||||
. "dnmshared"
|
||||
)
|
||||
|
||||
// 隐式导入
|
||||
var Constants *constants
|
||||
|
||||
func MustParseConstants() {
|
||||
@@ -24,13 +25,11 @@ func MustParseConstants() {
|
||||
if !isNotExist(fp) {
|
||||
testConstants := new(constants)
|
||||
loadJSON(fp, testConstants)
|
||||
//Logger.Debug(spew.Sdump(Constants))
|
||||
//Logger.Debug(spew.Sdump(testConstants))
|
||||
err := mergo.Merge(testConstants, Constants)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
Constants = testConstants
|
||||
//Logger.Debug("mergo.Merge", zap.Error(err))
|
||||
//Logger.Debug(spew.Sdump(testConstants))
|
||||
}
|
||||
}
|
||||
constantsPost()
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var Logger *zap.Logger
|
||||
var LoggerConfig zap.Config
|
||||
|
||||
func init() {
|
||||
LoggerConfig = zap.NewDevelopmentConfig()
|
||||
LoggerConfig.Level.SetLevel(zap.InfoLevel)
|
||||
LoggerConfig.Development = false
|
||||
LoggerConfig.Sampling = &zap.SamplingConfig{
|
||||
Initial: 100,
|
||||
Thereafter: 100,
|
||||
}
|
||||
LoggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
var err error
|
||||
Logger, err = LoggerConfig.Build()
|
||||
ErrFatal(err)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package utils
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
. "dnmshared"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
|
||||
@@ -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}$`)
|
||||
)
|
||||
|
||||
// 隐式导入
|
||||
var ConstVals = &struct {
|
||||
Player struct {
|
||||
CaptchaExpire time.Duration
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package env_tools
|
||||
|
||||
import (
|
||||
. "dnmshared"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
@@ -15,7 +16,9 @@ func LoadPreConf() {
|
||||
Logger.Info(`Merging PreConfSQLite data into MySQL`,
|
||||
zap.String("PreConfSQLitePath", Conf.General.PreConfSQLitePath))
|
||||
db, err := sqlx.Connect("sqlite3", Conf.General.PreConfSQLitePath)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
loadPreConfToMysql(db)
|
||||
@@ -39,7 +42,9 @@ func loadPreConfToMysql(db *sqlx.DB) {
|
||||
func loadSqlite(db *sqlx.DB, tbs []string) {
|
||||
for _, v := range tbs {
|
||||
result, err := storage.MySQLManagerIns.Exec("truncate " + v)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
Logger.Info("truncate", zap.Any("truncate "+v, result))
|
||||
query, args, err := sq.Select("*").From(v).ToSql()
|
||||
if err != nil {
|
||||
@@ -70,19 +75,25 @@ func createMysqlData(rows *sqlx.Rows, v string) {
|
||||
func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
||||
var ls []*dbBotPlayer
|
||||
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))
|
||||
for i, v := range ls {
|
||||
names[i] = v.Name
|
||||
}
|
||||
sql := "SELECT name FROM `player` WHERE name in (?)"
|
||||
query, args, err := sqlx.In(sql, names)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
query = storage.MySQLManagerIns.Rebind(query)
|
||||
// existNames := make([]string, len(ls), len(ls))
|
||||
var existPlayers []*models.Player
|
||||
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, botPlayer := range ls {
|
||||
var flag bool
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package env_tools
|
||||
|
||||
import (
|
||||
. "dnmshared"
|
||||
. "server/common"
|
||||
"server/common/utils"
|
||||
"server/models"
|
||||
@@ -15,7 +16,9 @@ func MergeTestPlayerAccounts() {
|
||||
fp := Conf.General.TestEnvSQLitePath
|
||||
Logger.Info(`Initializing TestPlayerAccounts in runtime MySQLServer from SQLite file:`, zap.String("fp", fp))
|
||||
db, err := sqlx.Connect("sqlite3", fp)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
maybeCreateNewPlayer(db, "test_player")
|
||||
}
|
||||
@@ -29,31 +32,35 @@ type dbTestPlayer struct {
|
||||
func maybeCreateNewPlayer(db *sqlx.DB, tableName string) {
|
||||
var ls []*dbTestPlayer
|
||||
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))
|
||||
for i, v := range ls {
|
||||
names[i] = v.Name
|
||||
}
|
||||
sql := "SELECT name FROM `player` WHERE name in (?)"
|
||||
query, args, err := sqlx.In(sql, names)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
query = storage.MySQLManagerIns.Rebind(query)
|
||||
// existNames := make([]string, len(ls), len(ls))
|
||||
var existPlayers []*models.Player
|
||||
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, testPlayer := range ls {
|
||||
var flag bool
|
||||
for _, v := range existPlayers {
|
||||
if testPlayer.Name == v.Name {
|
||||
// 已有数据,合并处理
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
// 找不到,新增
|
||||
Logger.Debug("create", zap.Any(tableName, testPlayer))
|
||||
err := createNewPlayer(testPlayer)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,6 +20,8 @@ require (
|
||||
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713
|
||||
go.uber.org/zap v1.9.1
|
||||
google.golang.org/protobuf v1.28.1
|
||||
|
||||
dnmshared v0.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -43,3 +45,7 @@ require (
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
dnmshared => ../dnmshared
|
||||
)
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
. "dnmshared"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/robfig/cron"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
. "dnmshared"
|
||||
)
|
||||
|
||||
type Barrier struct {
|
||||
Boundary *Polygon2D
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Use type `float64` for json unmarshalling of numbers.
|
||||
type Direction struct {
|
||||
Dx int32 `json:"dx,omitempty"`
|
||||
Dy int32 `json:"dy,omitempty"`
|
||||
}
|
||||
|
||||
type Vec2D struct {
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
}
|
||||
|
||||
type Polygon2D struct {
|
||||
Anchor *Vec2D `json:"-"` // This "Polygon2D.Anchor" is used to be assigned to "B2BodyDef.Position", which in turn is used as the position of the FIRST POINT of the polygon.
|
||||
Points []*Vec2D `json:"-"`
|
||||
|
||||
/*
|
||||
When used to represent a "polyline directly drawn in a `Tmx file`", we can initialize both "Anchor" and "Points" simultaneously.
|
||||
|
||||
Yet when used to represent a "polyline drawn in a `Tsx file`", we have to first initialize "Points w.r.t. center of the tile-rectangle", and then "Anchor(initially nil) of the tile positioned in the `Tmx file`".
|
||||
|
||||
Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for more information.
|
||||
*/
|
||||
|
||||
/*
|
||||
[WARNING] Used to cache "`TileWidth & TileHeight` of a Tsx file" only.
|
||||
*/
|
||||
TileWidth int
|
||||
TileHeight int
|
||||
|
||||
/*
|
||||
[WARNING] Used to cache "`Width & TileHeight` of an object in Tmx file" only.
|
||||
*/
|
||||
TmxObjectWidth float64
|
||||
TmxObjectHeight float64
|
||||
}
|
||||
|
||||
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
|
||||
dx := pt1.X - pt2.X
|
||||
dy := pt1.Y - pt2.Y
|
||||
return math.Sqrt(dx*dx + dy*dy)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
. "dnmshared"
|
||||
pb "server/pb_output"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
. "dnmshared"
|
||||
"fmt"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
@@ -2,7 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
. "server/common"
|
||||
. "dnmshared"
|
||||
"server/storage"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
. "dnmshared"
|
||||
"errors"
|
||||
. "server/common"
|
||||
"server/common/utils"
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
. "dnmshared"
|
||||
"fmt"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/solarlune/resolv"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
. "server/common"
|
||||
"server/common/utils"
|
||||
pb "server/pb_output"
|
||||
@@ -19,6 +16,11 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -261,7 +263,9 @@ func (pR *Room) ChooseStage() error {
|
||||
* -- YFLu, 2019-09-04
|
||||
*/
|
||||
pwd, err := os.Getwd()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().Unix())
|
||||
stageNameList := []string{ /*"pacman" ,*/ "richsoil"}
|
||||
|
||||
@@ -2,9 +2,9 @@ package models
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
. "dnmshared"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
. "server/common"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,445 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
. "server/common"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
LOW_SCORE_TREASURE_TYPE = 1
|
||||
HIGH_SCORE_TREASURE_TYPE = 2
|
||||
SPEED_SHOES_TYPE = 3
|
||||
|
||||
LOW_SCORE_TREASURE_SCORE = 100
|
||||
HIGH_SCORE_TREASURE_SCORE = 200
|
||||
|
||||
FLIPPED_HORIZONTALLY_FLAG uint32 = 0x80000000
|
||||
FLIPPED_VERTICALLY_FLAG uint32 = 0x40000000
|
||||
FLIPPED_DIAGONALLY_FLAG uint32 = 0x20000000
|
||||
)
|
||||
|
||||
// For either a "*.tmx" or "*.tsx" file. [begins]
|
||||
type TmxOrTsxProperty struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
type TmxOrTsxProperties struct {
|
||||
Property []*TmxOrTsxProperty `xml:"property"`
|
||||
}
|
||||
|
||||
type TmxOrTsxPolyline struct {
|
||||
Points string `xml:"points,attr"`
|
||||
}
|
||||
|
||||
type TmxOrTsxObject struct {
|
||||
Id int `xml:"id,attr"`
|
||||
Gid *int `xml:"gid,attr"`
|
||||
X float64 `xml:"x,attr"`
|
||||
Y float64 `xml:"y,attr"`
|
||||
Properties *TmxOrTsxProperties `xml:"properties"`
|
||||
Polyline *TmxOrTsxPolyline `xml:"polyline"`
|
||||
Width *float64 `xml:"width,attr"`
|
||||
Height *float64 `xml:"height,attr"`
|
||||
}
|
||||
|
||||
type TmxOrTsxObjectGroup struct {
|
||||
Draworder string `xml:"draworder,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Objects []*TmxOrTsxObject `xml:"object"`
|
||||
}
|
||||
|
||||
type TmxOrTsxImage struct {
|
||||
Source string `xml:"source,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
}
|
||||
|
||||
// For either a "*.tmx" or "*.tsx" file. [ends]
|
||||
|
||||
// Within a "*.tsx" file. [begins]
|
||||
type Tsx struct {
|
||||
Name string `xml:"name,attr"`
|
||||
TileWidth int `xml:"tilewidth,attr"`
|
||||
TileHeight int `xml:"tileheight,attr"`
|
||||
TileCount int `xml:"tilecount,attr"`
|
||||
Columns int `xml:"columns,attr"`
|
||||
Image []*TmxOrTsxImage `xml:"image"`
|
||||
Tiles []*TsxTile `xml:"tile"`
|
||||
}
|
||||
|
||||
type TsxTile struct {
|
||||
Id int `xml:"id,attr"`
|
||||
ObjectGroup *TmxOrTsxObjectGroup `xml:"objectgroup"`
|
||||
Properties *TmxOrTsxProperties `xml:"properties"`
|
||||
}
|
||||
|
||||
// Within a "*.tsx" file. [ends]
|
||||
|
||||
// Within a "*.tmx" file. [begins]
|
||||
type TmxLayerDecodedTileData struct {
|
||||
Id uint32
|
||||
Tileset *TmxTileset
|
||||
FlipHorizontal bool
|
||||
FlipVertical bool
|
||||
FlipDiagonal bool
|
||||
}
|
||||
|
||||
type TmxLayerEncodedData struct {
|
||||
Encoding string `xml:"encoding,attr"`
|
||||
Compression string `xml:"compression,attr"`
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type TmxLayer struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Data *TmxLayerEncodedData `xml:"data"`
|
||||
Tile []*TmxLayerDecodedTileData
|
||||
}
|
||||
|
||||
type TmxTileset struct {
|
||||
FirstGid uint32 `xml:"firstgid,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
TileWidth int `xml:"tilewidth,attr"`
|
||||
TileHeight int `xml:"tileheight,attr"`
|
||||
Images []*TmxOrTsxImage `xml:"image"`
|
||||
Source string `xml:"source,attr"`
|
||||
}
|
||||
|
||||
type TmxMap struct {
|
||||
Version string `xml:"version,attr"`
|
||||
Orientation string `xml:"orientation,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
TileWidth int `xml:"tilewidth,attr"`
|
||||
TileHeight int `xml:"tileheight,attr"`
|
||||
Properties []*TmxOrTsxProperties `xml:"properties"`
|
||||
Tilesets []*TmxTileset `xml:"tileset"`
|
||||
Layers []*TmxLayer `xml:"layer"`
|
||||
ObjectGroups []*TmxOrTsxObjectGroup `xml:"objectgroup"`
|
||||
}
|
||||
|
||||
// Within a "*.tmx" file. [ends]
|
||||
|
||||
func (d *TmxLayerEncodedData) decodeBase64() ([]byte, error) {
|
||||
r := bytes.NewReader([]byte(strings.TrimSpace(d.Value)))
|
||||
decr := base64.NewDecoder(base64.StdEncoding, r)
|
||||
if d.Compression == "zlib" {
|
||||
rclose, err := zlib.NewReader(decr)
|
||||
if err != nil {
|
||||
Logger.Error("tmx data decode zlib error: ", zap.Any("encoding", d.Encoding), zap.Any("compression", d.Compression), zap.Any("value", d.Value))
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadAll(rclose)
|
||||
}
|
||||
Logger.Error("tmx data decode invalid compression: ", zap.Any("encoding", d.Encoding), zap.Any("compression", d.Compression), zap.Any("value", d.Value))
|
||||
return nil, errors.New("Invalid compression.")
|
||||
}
|
||||
|
||||
func (l *TmxLayer) decodeBase64() ([]uint32, error) {
|
||||
databytes, err := l.Data.decodeBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l.Width == 0 || l.Height == 0 {
|
||||
return nil, errors.New("Zero width or height.")
|
||||
}
|
||||
if len(databytes) != l.Height*l.Width*4 {
|
||||
Logger.Error("TmxLayer decodeBase64 invalid data bytes:", zap.Any("width", l.Width), zap.Any("height", l.Height), zap.Any("data lenght", len(databytes)))
|
||||
return nil, errors.New("Data length error.")
|
||||
}
|
||||
dindex := 0
|
||||
gids := make([]uint32, l.Height*l.Width)
|
||||
for h := 0; h < l.Height; h++ {
|
||||
for w := 0; w < l.Width; w++ {
|
||||
gid := uint32(databytes[dindex]) |
|
||||
uint32(databytes[dindex+1])<<8 |
|
||||
uint32(databytes[dindex+2])<<16 |
|
||||
uint32(databytes[dindex+3])<<24
|
||||
dindex += 4
|
||||
gids[h*l.Width+w] = gid
|
||||
}
|
||||
}
|
||||
return gids, nil
|
||||
}
|
||||
|
||||
type Vec2DList []*Vec2D
|
||||
type Polygon2DList []*Polygon2D
|
||||
type StrToVec2DListMap map[string]*Vec2DList // Note that it's deliberately NOT using "map[string]Vec2DList", for the easy of passing return value to "models/room.go".
|
||||
type StrToPolygon2DListMap map[string]*Polygon2DList // Note that it's deliberately NOT using "map[string]Polygon2DList", for the easy of passing return value to "models/room.go".
|
||||
|
||||
func tmxPolylineToPolygon2D(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
|
||||
if nil == targetPolyline {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
singleValueArray := strings.Split(targetPolyline.Points, " ")
|
||||
|
||||
theUntransformedAnchor := &Vec2D{
|
||||
X: singleObjInTmxFile.X,
|
||||
Y: singleObjInTmxFile.Y,
|
||||
}
|
||||
theTransformedAnchor := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedAnchor)
|
||||
thePolygon2DFromPolyline := &Polygon2D{
|
||||
Anchor: &theTransformedAnchor,
|
||||
Points: make([]*Vec2D, len(singleValueArray)),
|
||||
}
|
||||
|
||||
for k, value := range singleValueArray {
|
||||
thePolygon2DFromPolyline.Points[k] = &Vec2D{}
|
||||
for kk, v := range strings.Split(value, ",") {
|
||||
coordinateValue, err := strconv.ParseFloat(v, 64)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
if 0 == (kk % 2) {
|
||||
thePolygon2DFromPolyline.Points[k].X = (coordinateValue)
|
||||
} else {
|
||||
thePolygon2DFromPolyline.Points[k].Y = (coordinateValue)
|
||||
}
|
||||
}
|
||||
|
||||
tmp := &Vec2D{
|
||||
X: thePolygon2DFromPolyline.Points[k].X,
|
||||
Y: thePolygon2DFromPolyline.Points[k].Y,
|
||||
}
|
||||
transformedTmp := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(tmp)
|
||||
thePolygon2DFromPolyline.Points[k].X = transformedTmp.X
|
||||
thePolygon2DFromPolyline.Points[k].Y = transformedTmp.Y
|
||||
}
|
||||
|
||||
return thePolygon2DFromPolyline, nil
|
||||
}
|
||||
|
||||
func tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) {
|
||||
if nil == targetPolyline {
|
||||
return nil, nil
|
||||
}
|
||||
var factorHalf float64 = 0.5
|
||||
offsetFromTopLeftInTileLocalCoordX := singleObjInTsxFile.X
|
||||
offsetFromTopLeftInTileLocalCoordY := singleObjInTsxFile.Y
|
||||
|
||||
singleValueArray := strings.Split(targetPolyline.Points, " ")
|
||||
pointsCount := len(singleValueArray)
|
||||
|
||||
thePolygon2DFromPolyline := &Polygon2D{
|
||||
Anchor: nil,
|
||||
Points: make([]*Vec2D, pointsCount),
|
||||
TileWidth: pTsxIns.TileWidth,
|
||||
TileHeight: pTsxIns.TileHeight,
|
||||
}
|
||||
|
||||
/*
|
||||
[WARNING] In this case, the "Treasure"s and "GuardTower"s are put into Tmx file as "ImageObject"s, of each the "ProportionalAnchor" is (0.5, 0). Therefore the "thePolygon2DFromPolyline.Points" are "offsets w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details.
|
||||
*/
|
||||
|
||||
for k, value := range singleValueArray {
|
||||
thePolygon2DFromPolyline.Points[k] = &Vec2D{}
|
||||
for kk, v := range strings.Split(value, ",") {
|
||||
coordinateValue, err := strconv.ParseFloat(v, 64)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
if 0 == (kk % 2) {
|
||||
// W.r.t. center.
|
||||
thePolygon2DFromPolyline.Points[k].X = (coordinateValue + offsetFromTopLeftInTileLocalCoordX) - factorHalf*float64(pTsxIns.TileWidth)
|
||||
} else {
|
||||
// W.r.t. bottom.
|
||||
thePolygon2DFromPolyline.Points[k].Y = float64(pTsxIns.TileHeight) - (coordinateValue + offsetFromTopLeftInTileLocalCoordY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return thePolygon2DFromPolyline, nil
|
||||
}
|
||||
|
||||
func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMap map[int]StrToPolygon2DListMap) error {
|
||||
pTsxIns := &Tsx{}
|
||||
err := xml.Unmarshal(byteArrOfTsxFile, pTsxIns)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
/*
|
||||
// For debug-printing only. -- YFLu, 2019-09-04.
|
||||
|
||||
reserializedTmxMap, err := pTmxMapIns.ToXML()
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
|
||||
for _, tile := range pTsxIns.Tiles {
|
||||
globalGid := (firstGid + int(tile.Id))
|
||||
/**
|
||||
A tile xml string could be
|
||||
|
||||
```
|
||||
<tile id="13">
|
||||
<objectgroup draworder="index">
|
||||
<object id="1" x="-154" y="-159">
|
||||
<properties>
|
||||
<property name="boundary_type" value="guardTower"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -95,179 18,407 361,434 458,168 333,-7"/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</tile>
|
||||
```
|
||||
, we currently REQUIRE that "`an object of a tile` with ONE OR MORE polylines must come with a single corresponding '<property name=`type` value=`...` />', and viceversa".
|
||||
|
||||
Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D".
|
||||
*/
|
||||
|
||||
theObjGroup := tile.ObjectGroup
|
||||
if nil == theObjGroup {
|
||||
continue
|
||||
}
|
||||
for _, singleObj := range theObjGroup.Objects {
|
||||
if nil == singleObj.Polyline {
|
||||
// Temporarily omit those non-polyline-containing objects.
|
||||
continue
|
||||
}
|
||||
if nil == singleObj.Properties.Property || "boundary_type" != singleObj.Properties.Property[0].Name {
|
||||
continue
|
||||
}
|
||||
|
||||
key := singleObj.Properties.Property[0].Value
|
||||
|
||||
var theStrToPolygon2DListMap StrToPolygon2DListMap
|
||||
if existingStrToPolygon2DListMap, ok := gidBoundariesMap[globalGid]; ok {
|
||||
theStrToPolygon2DListMap = existingStrToPolygon2DListMap
|
||||
} else {
|
||||
gidBoundariesMap[globalGid] = make(StrToPolygon2DListMap, 0)
|
||||
theStrToPolygon2DListMap = gidBoundariesMap[globalGid]
|
||||
}
|
||||
|
||||
var pThePolygon2DList *Polygon2DList
|
||||
if _, ok := theStrToPolygon2DListMap[key]; ok {
|
||||
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
||||
} else {
|
||||
thePolygon2DList := make(Polygon2DList, 0)
|
||||
theStrToPolygon2DListMap[key] = &thePolygon2DList
|
||||
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
||||
}
|
||||
|
||||
thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
*pThePolygon2DList = append(*pThePolygon2DList, thePolygon2DFromPolyline)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) {
|
||||
toRetStrToVec2DListMap := make(StrToVec2DListMap, 0)
|
||||
toRetStrToPolygon2DListMap := make(StrToPolygon2DListMap, 0)
|
||||
|
||||
for _, objGroup := range pTmxMapIns.ObjectGroups {
|
||||
switch objGroup.Name {
|
||||
case "PlayerStartingPos":
|
||||
var pTheVec2DListToCache *Vec2DList
|
||||
_, ok := toRetStrToVec2DListMap[objGroup.Name]
|
||||
if false == ok {
|
||||
theVec2DListToCache := make(Vec2DList, 0)
|
||||
toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache
|
||||
}
|
||||
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
|
||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||
theUntransformedPos := &Vec2D{
|
||||
X: singleObjInTmxFile.X,
|
||||
Y: singleObjInTmxFile.Y,
|
||||
}
|
||||
thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos)
|
||||
*pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld)
|
||||
}
|
||||
case "Barrier":
|
||||
// Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]".
|
||||
var pThePolygon2DListToCache *Polygon2DList
|
||||
_, ok := toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
if false == ok {
|
||||
thePolygon2DListToCache := make(Polygon2DList, 0)
|
||||
toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache
|
||||
}
|
||||
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
|
||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||
if nil == singleObjInTmxFile.Polyline {
|
||||
continue
|
||||
}
|
||||
if nil == singleObjInTmxFile.Properties.Property || "boundary_type" != singleObjInTmxFile.Properties.Property[0].Name || "barrier" != singleObjInTmxFile.Properties.Property[0].Value {
|
||||
continue
|
||||
}
|
||||
|
||||
thePolygon2DInWorld, err := tmxPolylineToPolygon2D(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return int32(pTmxMapIns.Width), int32(pTmxMapIns.Height), int32(pTmxMapIns.TileWidth), int32(pTmxMapIns.TileHeight), toRetStrToVec2DListMap, toRetStrToPolygon2DListMap, nil
|
||||
}
|
||||
|
||||
func (pTmxMap *TmxMap) ToXML() (string, error) {
|
||||
ret, err := xml.Marshal(pTmxMap)
|
||||
return string(ret[:]), err
|
||||
}
|
||||
|
||||
type TileRectilinearSize struct {
|
||||
Width float64
|
||||
Height float64
|
||||
}
|
||||
|
||||
func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerVec *Vec2D) Vec2D {
|
||||
var tileRectilinearSize TileRectilinearSize
|
||||
tileRectilinearSize.Width = float64(pTmxMapIns.TileWidth)
|
||||
tileRectilinearSize.Height = float64(pTmxMapIns.TileHeight)
|
||||
tileSizeUnifiedLength := math.Sqrt(tileRectilinearSize.Width*tileRectilinearSize.Width*0.25 + tileRectilinearSize.Height*tileRectilinearSize.Height*0.25)
|
||||
isometricObjectLayerPointOffsetScaleFactor := (tileSizeUnifiedLength / tileRectilinearSize.Height)
|
||||
cosineThetaRadian := (tileRectilinearSize.Width * 0.5) / tileSizeUnifiedLength
|
||||
sineThetaRadian := (tileRectilinearSize.Height * 0.5) / tileSizeUnifiedLength
|
||||
|
||||
transMat := [...][2]float64{
|
||||
{isometricObjectLayerPointOffsetScaleFactor * cosineThetaRadian, -isometricObjectLayerPointOffsetScaleFactor * cosineThetaRadian},
|
||||
{-isometricObjectLayerPointOffsetScaleFactor * sineThetaRadian, -isometricObjectLayerPointOffsetScaleFactor * sineThetaRadian},
|
||||
}
|
||||
convertedVecX := transMat[0][0]*continuousObjLayerVec.X + transMat[0][1]*continuousObjLayerVec.Y
|
||||
convertedVecY := transMat[1][0]*continuousObjLayerVec.X + transMat[1][1]*continuousObjLayerVec.Y
|
||||
converted := Vec2D{
|
||||
X: convertedVecX,
|
||||
Y: convertedVecY,
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continuousObjLayerOffset *Vec2D) Vec2D {
|
||||
layerOffset := Vec2D{
|
||||
X: 0,
|
||||
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||
}
|
||||
|
||||
calibratedVec := continuousObjLayerOffset
|
||||
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(calibratedVec)
|
||||
|
||||
toRet := Vec2D{
|
||||
X: layerOffset.X + convertedVec.X,
|
||||
Y: layerOffset.Y + convertedVec.Y,
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
|
||||
. "dnmshared"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -15,8 +17,12 @@ var (
|
||||
func initMySQL() {
|
||||
var err error
|
||||
MySQLManagerIns, err = sqlx.Connect("mysql", Conf.MySQL.DSN+"?charset=utf8mb4")
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
err = MySQLManagerIns.Ping()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
Logger.Info("MySQLManagerIns", zap.Any("mysql", MySQLManagerIns))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/go-redis/redis"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
. "dnmshared"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,6 +22,8 @@ func initRedis() {
|
||||
DB: Conf.Redis.Dbname, // use default DB
|
||||
})
|
||||
pong, err := RedisManagerIns.Ping().Result()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
Logger.Info("Redis", zap.String("ping", pong))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ func loadTMX(fp string, pTmxMapIns *models.TmxMap) {
|
||||
}
|
||||
|
||||
byteArr, err := ioutil.ReadFile(fp)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
models.DeserializeToTmxMapIns(byteArr, pTmxMapIns)
|
||||
for _, info := range pTmxMapIns.TreasuresInfo {
|
||||
fmt.Printf("treasuresInfo: %v\n", info)
|
||||
@@ -33,7 +35,9 @@ func loadTSX(fp string, pTsxIns *models.Tsx) {
|
||||
}
|
||||
|
||||
byteArr, err := ioutil.ReadFile(fp)
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
models.DeserializeToTsxIns(byteArr, pTsxIns)
|
||||
for _, Pos := range pTsxIns.TrapPolyLineList {
|
||||
fmt.Printf("%v\n", Pos)
|
||||
@@ -43,10 +47,14 @@ func loadTSX(fp string, pTsxIns *models.Tsx) {
|
||||
func getTMXInfo() {
|
||||
relativePath = "../frontend/assets/resources/map/treasurehunter.tmx"
|
||||
execPath, err := os.Executable()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("execPath = %v, pwd = %s, returning...\n", execPath, pwd)
|
||||
|
||||
@@ -61,10 +69,14 @@ func getTSXInfo() {
|
||||
|
||||
relativePath = "../frontend/assets/resources/map/tile_1.tsx"
|
||||
execPath, err := os.Executable()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
ErrFatal(err)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("execPath = %v, pwd = %s, returning...\n", execPath, pwd)
|
||||
tsxIns := models.Tsx{}
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
. "dnmshared"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user