Compare commits

...

93 Commits
v0.1 ... v0.6.3

Author SHA1 Message Date
genxium
d21f59cafa Minor fix. 2022-11-20 21:07:45 +08:00
Wing
335fef66ef Merge pull request #7 from genxium/dungeon_characters
Updated anim of characters, and minor updates to inputs on wire to support atk button.
2022-11-20 20:18:44 +08:00
genxium
52480ab29f Updated TouchEventsManager to support input from Keyboard as well as an additional atk btn. 2022-11-20 20:13:08 +08:00
genxium
971f6461ab Updated charts. 2022-11-20 12:27:29 +08:00
genxium
061aa449c9 Fixes for online map class to use updated character animations. 2022-11-19 22:59:12 +08:00
genxium
78dd9ecd85 Initial commit for offline map, might break the online version. 2022-11-19 20:58:07 +08:00
yflu
d4226137b6 Initial draft of an offline map for testing new characters. 2022-11-17 23:39:32 +08:00
yflu
e432026fec Fixed proto_gen_shortcut script for OSX. 2022-11-17 23:13:53 +08:00
Wing
3e7718ed04 Merge pull request #6 from genxium/dungeon_battle
Added support of ORTHO orientation tmx.
2022-11-17 16:51:11 +08:00
genxium
b78dd54431 Regenerated new resource ccc meta files. 2022-11-17 15:10:17 +08:00
genxium
22fb72afbc Fixed frontend tmx parsing for ortho map. 2022-11-17 15:01:35 +08:00
genxium
7b9172c27b Fixed backend tmx parsing for ortho map. 2022-11-16 21:32:25 +08:00
genxium
7e12853a73 Added fineart resources. 2022-11-16 20:58:12 +08:00
genxium
95dcc2ef17 Updated README. 2022-11-13 22:23:54 +08:00
Wing
63164569b1 Merge pull request #5 from genxium/integer_positioning
Integer positioning.
2022-11-13 14:14:50 +08:00
genxium
8a4efd023b Updated README. 2022-11-13 14:13:19 +08:00
genxium
b031fc1c61 Minor fix on backend coordinate ratio conversion. 2022-11-13 13:06:52 +08:00
genxium
4369729d9c Fixed recovery upon reconnection. 2022-11-13 11:37:30 +08:00
genxium
2d080ad134 Fixed backend collision constants. 2022-11-12 22:53:35 +08:00
genxium
bd9beec5e5 Added more helper functions for backend collision debugging. 2022-11-12 20:34:38 +08:00
genxium
89a54211e1 Aligned constants used for backend collision unit-testing. 2022-11-12 12:18:00 +08:00
genxium
a4ebde3e07 Updated CLI unit tests again. 2022-11-12 11:41:18 +08:00
genxium
41967b11f7 Updated CLI unit tests. 2022-11-12 11:20:16 +08:00
genxium
98daeff408 Updated frontend logging. 2022-11-11 23:43:51 +08:00
genxium
320e98361e Fixes for resync. 2022-11-11 20:10:43 +08:00
genxium
15a062af10 Completed frontend refactoring for more convenient unit testing. 2022-11-11 13:27:48 +08:00
yflu
3f4e49656a Temp broken commit during frontend refactoring. 2022-11-11 00:04:00 +08:00
genxium
f97ce22cef Refactored backend for convenience of unit-testing. 2022-11-10 21:28:46 +08:00
genxium
901b189c5a Improvements on using integer positioning. 2022-11-10 18:18:00 +08:00
genxium
e5ed8124e8 Minor updates to frontend collider position reset pace. 2022-11-09 23:53:45 +08:00
genxium
885443c2b1 Fixes to frontend coordinate conversion. 2022-11-09 23:46:11 +08:00
genxium
aa795fcee5 Fixed frontend compilation. 2022-11-09 18:13:53 +08:00
genxium
cb3c19a339 Fixed Golang part compilation. 2022-11-09 14:20:26 +08:00
yflu
d3d3629618 A broken commit. 2022-11-09 12:19:29 +08:00
genxium
f37f4337de Minor update to floating number precision. 2022-11-08 21:38:23 +08:00
genxium
1a3b3a0a7a Fixed multi-barrier collision handling in backend. 2022-11-06 13:42:40 +08:00
genxium
4f1ce0d71a Fixed typos. 2022-11-03 10:06:21 +08:00
genxium
1f728071a9 Updated charts. 2022-11-03 00:16:03 +08:00
Wing
4b68917337 Fixed typo. 2022-11-02 12:40:26 +08:00
genxium
0cbf968228 Updated README. 2022-11-02 10:57:37 +08:00
genxium
ec2a21dbe7 Minor fix for "false == Room.BackendDynamicsEnabled". 2022-10-31 09:02:14 +08:00
Wing
dc6402c2b7 Updated README. 2022-10-26 10:28:32 +08:00
yflu
8038b393e0 Formatted codes. 2022-10-25 23:36:55 +08:00
yflu
4e0f7b52d4 Fixed frontend ws session onclose handling. 2022-10-25 23:02:39 +08:00
genxium
486c46f608 Added backend dynamics toggle. 2022-10-25 10:42:36 +08:00
genxium
6d075877ec Fixed frontend reconnection on page refresh for Firefox and Safari. 2022-10-25 09:52:38 +08:00
yflu
fe826b393b Updated logs in frontend. 2022-10-25 00:05:38 +08:00
Wing
c69aa25353 Merge pull request #4 from genxium/backend_collision_pushback
Backend collision pushback synchronization.
2022-10-22 13:52:52 +08:00
yflu
0f4d067c06 Updated test cases for frontend-backend-collision-reconciliation. 2022-10-22 13:38:10 +08:00
yflu
cff31d295c Simplified frontend log. 2022-10-22 00:03:26 +08:00
yflu
150e30db2a Drafted backend collision with pushback calculations. 2022-10-21 22:39:08 +08:00
genxium
bc8989a0e6 Minor update. 2022-10-19 17:56:52 +08:00
genxium
1959a7fd9a Refactored use of SAT collision checking. 2022-10-19 17:32:18 +08:00
genxium
3baaf1d52c Updated collider utility encapsulation for visualization subproject. 2022-10-19 15:10:11 +08:00
genxium
62f10e0877 Added collision test in collider visualizer. 2022-10-19 09:48:52 +08:00
genxium
c3c7854e92 Simplified frontend codes. 2022-10-16 10:38:38 +08:00
genxium
cb794d70c7 Updated video demo link. 2022-10-15 22:00:59 +08:00
Wing
0047259e9f Merge pull request #3 from genxium/backend_collision
Added backend collision handling.
2022-10-15 21:56:23 +08:00
genxium
232d8ad148 Fixes for backend collision handling. 2022-10-15 21:39:22 +08:00
genxium
d49e7830d4 Fixes for collider polygonal shape drawing. 2022-10-15 16:51:38 +08:00
genxium
1122f4d71c Added simple map for calibration. 2022-10-15 00:35:32 +08:00
genxium
f2c8d4cd65 Minor updates on logging. 2022-10-14 18:47:09 +08:00
genxium
4e7f9e63ac Made collider visualizer show the actual collider geometry from config file. 2022-10-14 18:02:03 +08:00
genxium
e762d257a6 Refactored module structure for ease of testing backend colliders. 2022-10-14 16:08:22 +08:00
genxium
286944b88c Added basic backend collider visualizer. 2022-10-14 11:49:04 +08:00
genxium
05dc593d2c Temp commit. 2022-10-10 21:58:29 +08:00
genxium
5f9aaddc9c Fixed clock sync and camera tracking. 2022-10-10 14:33:04 +08:00
genxium
e224aaf680 Updated pb schema. 2022-10-10 12:17:23 +08:00
yflu
9c07b43157 Minor update. 2022-10-07 10:36:19 +08:00
yflu
3203ea9f1e Fixed typo. 2022-10-07 10:10:33 +08:00
genxium
d90c4ead91 Updated README. 2022-10-05 23:43:38 +08:00
Wing
1e5d7d1d06 Merge pull request #2 from genxium/backend_render_frame_calc
Added rejoining feature.
2022-10-04 11:31:27 +08:00
genxium
a6731dc7d6 Minor fixes for rejoining signals. 2022-10-04 11:24:47 +08:00
genxium
1004fd45db Fixed timing for upsync confirmation merge. 2022-10-03 23:54:38 +08:00
genxium
09b12c5b16 Updated documentation. 2022-10-03 20:25:51 +08:00
genxium
9d9bea21ef Minor updates. 2022-10-03 00:22:05 +08:00
genxium
54d6e52498 Minor fix for frontend chaserRenderFrameId positioning and logging. 2022-10-02 16:19:54 +08:00
genxium
4d1de44ee5 Updated config uniformity. 2022-10-02 11:33:40 +08:00
genxium
f3a576ba13 Minor updates. 2022-10-01 23:54:48 +08:00
genxium
2264c0d362 Updated logging. 2022-10-01 20:45:52 +08:00
genxium
cd83539197 Fixed some trivial runtime errors. 2022-10-01 17:26:37 +08:00
genxium
a2a8be9068 Added backend collider initialization codes. 2022-10-01 15:14:05 +08:00
Wing
527cc94242 Minor fix on markdown file. 2022-09-30 23:32:26 +08:00
genxium
266335b7c6 Refactoring backend for periodical force confirmation. 2022-09-30 23:25:32 +08:00
genxium
14fb8e94b2 Preparation of server-side collision calc. 2022-09-26 23:09:18 +08:00
Wing
ff092a40ed Merge pull request #1 from genxium/manual_collision
Merge the manual collision.
2022-09-26 11:49:55 +08:00
genxium
80dc05a92b Fixed collision detection and camera tracking. 2022-09-26 11:42:48 +08:00
genxium
2dfd6083c5 Fixed a few indexing errors. 2022-09-26 10:36:46 +08:00
genxium
50273c981b Minor fix. 2022-09-25 23:30:34 +08:00
genxium
114e6b0501 Updated documentation. 2022-09-25 23:21:12 +08:00
genxium
cccbeb1c29 Fixed part of frame chasing dynamics, yet collision handling is still broken. 2022-09-25 20:48:09 +08:00
genxium
1cc0eed39e A temp commit after coping with many obvious runtime errors. 2022-09-25 17:16:44 +08:00
genxium
85c94a9e5d Preparation for changing collision system. 2022-09-23 16:42:44 +08:00
350 changed files with 15676 additions and 24722 deletions

43
ConcerningEdgeCases.md Normal file
View File

@@ -0,0 +1,43 @@
# Potential avalanche from local lag
Under the current "input delay" algorithm, the lag of a single player would cause all the other players to receive outdated commands, e.g. when at a certain moment
- player#1: renderFrameId = 100, significantly lagged due to local CPU overheated
- player#2: renderFrameId = 240
- player#3: renderFrameId = 239
- player#4: renderFrameId = 242
players #2, #3 #4 would receive "outdated(in their subjective feelings) but all-confirmed commands" from then on, thus forced to rollback and chase many frames - the lag due to "large range of frame-chasing" would then further deteriorate the situation - like an avalanche.
In a "no-server & p2p" setup, I couldn't think of a proper way to cope with such edge case. Solely on the frontend we could only mitigate the impact to players #2, #3, #4, e.g. a potential lag due to "large range of frame-chasing" is proactively avoided in `<proj-root>/frontend/assets/scripts/Map.js, function update(dt)`.
However in a "server as authority" setup, the server could force confirming an inputFrame without player#1's upsync, and notify player#1 to apply a "roomDownsyncFrame" as well as drop all its outdated local inputFrames.
# Start up frames
renderFrameId | generatedInputFrameId | toApplyInputFrameId
-------------------|----------------------------|----------------------
0, 1, 2, 3 | 0, _EMP_, _EMP_, _EMP_ | 0
4, 5, 6, 7 | 1, _EMP_, _EMP_, _EMP_ | 0
8, 9, 10, 11 | 2, _EMP_, _EMP_, _EMP_ | 1
12, 13, 14, 15 | 3, _EMP_, _EMP_, _EMP_ | 2
It should be reasonable to assume that inputFrameId=0 is always of all-empty content, because human has no chance of clicking at the very first render frame.
# Alignment of the current setup
The following setup is chosen deliberately for some "%4" number coincidence.
- NstDelayFrames = 2
- InputDelayFrames = 4
- InputScaleFrames = 2
If "InputDelayFrames" is changed, the impact would be as follows, kindly note that "372%4 == 0".
### pR.InputDelayFrames = 4
renderFrameId | toApplyInputFrameId
--------------------------|----------------------------------------------------
368, 369, 370, 371 | 91
372, 373, 374, 375 | 92
### pR.InputDelayFrames = 5
renderFrameId | toApplyInputFrameId
--------------------------|----------------------------------------------------
..., ..., ..., 368 | 90
369, 370, 371, 372 | 91
373, 374, 375, ... | 92

View File

@@ -1,53 +1,89 @@
# 0. Preface
If you'd like to play with the backend code seriously, please read the detailed explanation of its important "lifecycle events" in [this note](https://app.yinxiang.com/fx/5c575124-01db-419b-9c02-ec81f78c6ddc).
# Preface
There could be some left over wechat-game related code pieces, but they're neither meant to work nor supported anymore.
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
# 1. Database Server
![gif_demo](./charts/along_wall_interaction_with_reconnection.gif)
The database product to be used for this project is MySQL 5.7.
Please also checkout [this demo video](https://pan.baidu.com/s/1YkfuHjNLzlFVnKiEj6wrDQ?pwd=tkr5) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
We use [skeema](https://github.com/skeema/skeema) for schematic synchronization under `<proj-root>/database/skeema-repo-root/` which intentionally doesn't contain a `.skeema` file. Please read [this tutorial](https://shimo.im/doc/wQ0LvB0rlZcbHF5V) for more information.
The video mainly shows the following features.
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
- The game is recovered for a player upon reconnection.
- Both backend(Golang) and frontend(JavaScript) execute collision detection and handle collision contacts by the same algorithm. The backend dynamics is togglable by [Room.BackendDynamicsEnabled](https://github.com/genxium/DelayNoMore/blob/v0.5.2/battle_srv/models/room.go#L813), but **when turned off the game couldn't support recovery upon reconnection**.
You can use [this node module (still under development)](https://github.com/genxium/node-mysqldiff-bridge) instead under `Windows10`, other versions of Windows are not yet tested for compatibility.
_(how input delay roughly works)_
The following command(s)
![input_delay_intro](./charts/InputDelayIntro.jpg)
_(how rollback-and-chase in this project roughly works)_
![rollback_and_chase_intro](./charts/RollbackAndChase.jpg)
![floating_point_accumulation_err](./charts/AvoidingFloatingPointAccumulationErr.jpg)
# 1. Building & running
## 1.1 Tools to install
### Backend
- [Command Line Tools for Xcode](https://developer.apple.com/download/all/?q=command%20line%20tools) (on OSX) or [TDM-GCC](https://jmeubank.github.io/tdm-gcc/download/) (on Windows) (a `make` executable mandatory)
- [Golang1.19.1](https://golang.org/dl/) (mandatory, in China please try a mirror site like [that of ustc](https://mirrors.ustc.edu.cn/golang/))
- [MySQL 5.7](https://dev.mysql.com/downloads/windows/installer/5.7.html) (mandatory, for OSX not all versions of 5.7 can be found thus 5.7.24 is recommended)
- [Redis 3.0.503 or above](https://redis.io/download/) (mandatory)
- [skeema](https://www.skeema.io/) (optional, only for convenient MySQL schema provisioning)
- [protobuf CLI](https://developers.google.com/protocol-buffers/docs/downloads) (optional, only for development)
### Frontend
- [CocosCreator v2.2.1](https://www.cocos.com/en/cocos-creator-2-2-1-released-with-performance-improvements) (mandatory, **ONLY AVAILABLE on Windows or OSX and should be exactly this version**, DON'T use any other version because CocosCreator is well-known for new versions not being backward compatible)
- [protojs](https://www.npmjs.com/package/protojs) (optional, only for development)
## 1.2 Provisioning
### Backend/Database
It's strongly recommended that `skeema` is used for provisioning [the required schema](https://github.com/genxium/DelayNoMore/tree/main/database/skeema-repo-root) in MySQL instance. When using `skeema` the steps are as follows.
```
### Optional.
### Mandatory after an initial clone
user@proj-root/database/skeema-repo-root> cp .skeema.template .skeema
###
user@proj-root/database/skeema-repo-root> skeema diff
### Mandatory
user@proj-root/database/skeema-repo-root> skeema push
```
is recommended to be used for checking difference from your "live MySQL server" to the latest expected schema tracked in git.
# 2. Building & running
On `Windows 10/11`, you can compile `skeema` from source and config the host to be `127.0.0.1` instead of `localhost` to use it, i.e. circumventing the pitfall for MySQL unix socket connection on Windows.
## 2.1 Golang1.11
See https://github.com/genxium/Go111ModulePrac for details.
### Backend/Golang
```
user@proj-root/battle_srv/configs> cp -r ./configs.template ./configs
```
## 2.2 MySQL
On a product machine, you can install and manage `MySQL` server by [these scripts](https://github.com/genxium/Ubuntu14InitScripts/tree/master/database/mysql).
### Frontend
```
user@proj-root/frontend/assets/plugin_scripts> cp ./conf.js.template ./conf.js
```
## 2.3 Required Config Files
## 1.2 Actual building & running
### Backend
```
### The following command runs mysql-server in foreground, it's almost NEVER run in such a way, please find a proper way to run it for yourself
user@anywhere> mysqld
### 2.3.1 Backend
- It needs `<proj-root>/battle_srv/configs/*` which is generated by `cd <proj-root>/battle_srv && cp -r ./configs.template ./configs` and necessary customization.
### The following command runs redis-server in foreground, it's OK to put it in background
user@anywhere> redis-server
### 2.3.2 Frontend
- It needs CocosCreator v2.2.1 to build.
- A required "CocosCreator plugin `i18n`" is already enclosed in the project, if you have a globally installed "CocosCreator plugin `i18n`"(often located at `$HOME/.CocosCreator/packages/`) they should be OK to co-exist.
- It needs `<proj-root>/frontend/assets/plugin_scripts/conf.js` which is generated by `cd <proj-root>/frontend/assets/plugin_scripts && cp conf.js.template conf.js`.
### on Windows using TDM-GCC: mingw32-make run-test
user@proj-root/battle_srv> make run-test
```
## 2.4 Troubleshooting
### Frontend
The easy way is to try out 2 players with test accounts on a same machine.
- Open CocosCreator v2.2.1 (mandatory, it serves the web content of the following steps)
- Open one browser instance, visit _http://localhost:7456?expectedRoomId=1_, input `add`on the username box and click to request a captcha, this is a test account so a captcha would be returned by the backend and filled automatically (as shown in the figure below), then click and click to proceed to a matching scene.
- Open another browser instance, visit _http://localhost:7456?expectedRoomId=1_, input `bdd`on the username box and click to request a captcha, this is another test account so a captcha would be returned by the backend and filled automatically, then click and click to proceed, when matched a `battle`(but no competition rule yet) would start.
- Try out the onscreen virtual joysticks to move the cars and see if their movements are in-sync.
![screenshot-2](./charts/screenshot-2.png)
### 2.4.1 Redis snapshot writing failure
## 2 Troubleshooting
### 2.1 Redis snapshot writing failure
```
ErrFatal {"err": "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error."}
```
Just restart your `redis-server` process.
# 3. Git configs cautions
Please make sure that you've set `ignorecase = false` in your `[core] section of <proj-root>/.git/config`.

View File

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

View File

@@ -1,6 +1,11 @@
package v1
import (
"battle_srv/api"
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/models"
"battle_srv/storage"
"bytes"
"crypto/sha256"
"encoding/hex"
@@ -10,12 +15,9 @@ import (
"go.uber.org/zap"
"io/ioutil"
"net/http"
"server/api"
. "server/common"
"server/common/utils"
"server/models"
"server/storage"
"strconv"
. "dnmshared"
)
var Player = playerController{}
@@ -77,7 +79,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
c.Set(api.RET, Constants.RetCode.UnknownError)
return
}
// Redis剩余时长校验
if ttl >= ConstVals.Player.CaptchaMaxTTL {
Logger.Info("There's an existing SmsCaptcha record in Redis-server: ", zap.String("key", redisKey), zap.Duration("ttl", ttl))
c.Set(api.RET, Constants.RetCode.SmsCaptchaRequestedTooFrequently)
@@ -87,7 +88,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
pass := false
var succRet int
if Conf.General.ServerEnv == SERVER_ENV_TEST {
// 测试环境,优先从数据库校验`player.name`不通过再走机器人magic name校验
player, err := models.GetPlayerByName(req.Num)
if nil == err && nil != player {
pass = true
@@ -96,7 +96,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
}
if !pass {
// 机器人magic name校验不通过再走手机号校验
player, err := models.GetPlayerByName(req.Num)
if nil == err && nil != player {
pass = true
@@ -109,7 +108,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
succRet = Constants.RetCode.Ok
pass = true
}
// Hardecoded 只验证国内手机号格式
if req.CountryCode == "86" {
if RE_CHINA_PHONE_NUM.MatchString(req.Num) {
succRet = Constants.RetCode.Ok
@@ -131,7 +129,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
}{Ret: succRet}
var captcha string
if ttl >= 0 {
// 已有未过期的旧验证码记录,续验证码有效期。
storage.RedisManagerIns.Expire(redisKey, ConstVals.Player.CaptchaExpire)
captcha = storage.RedisManagerIns.Get(redisKey).Val()
if ttl >= ConstVals.Player.CaptchaExpire/4 {
@@ -145,7 +142,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
}
Logger.Info("Extended ttl of existing SMSCaptcha record in Redis:", zap.String("key", redisKey), zap.String("captcha", captcha))
} else {
// 校验通过,进行验证码生成处理
captcha = strconv.Itoa(utils.Rand.Number(1000, 9999))
if succRet == Constants.RetCode.Ok {
getSmsCaptchaRespErrorCode := sendSMSViaVendor(req.Num, req.CountryCode, captcha)
@@ -232,7 +228,6 @@ func (p *playerController) WechatLogin(c *gin.Context) {
return
}
//baseInfo ResAccessToken 获取用户授权access_token的返回结果
baseInfo, err := utils.WechatIns.GetOauth2Basic(req.Authcode)
if err != nil {
@@ -248,7 +243,6 @@ func (p *playerController) WechatLogin(c *gin.Context) {
c.Set(api.RET, Constants.RetCode.WechatServerError)
return
}
//fserver不会返回openId
userInfo.OpenID = baseInfo.OpenID
player, err := p.maybeCreatePlayerWechatAuthBinding(userInfo)
@@ -314,7 +308,6 @@ func (p *playerController) WechatGameLogin(c *gin.Context) {
return
}
//baseInfo ResAccessToken 获取用户授权access_token的返回结果
baseInfo, err := utils.WechatGameIns.GetOauth2Basic(req.Authcode)
if err != nil {
@@ -335,7 +328,6 @@ func (p *playerController) WechatGameLogin(c *gin.Context) {
c.Set(api.RET, Constants.RetCode.WechatServerError)
return
}
//fserver不会返回openId
userInfo.OpenID = baseInfo.OpenID
player, err := p.maybeCreatePlayerWechatGameAuthBinding(userInfo)
@@ -393,7 +385,6 @@ func (p *playerController) IntAuthTokenLogin(c *gin.Context) {
return
}
//kobako: 从player获取display name等
player, err := models.GetPlayerById(playerLogin.PlayerID)
if err != nil {
Logger.Error("Get player by id in IntAuthTokenLogin function error: ", zap.Error(err))
@@ -477,7 +468,6 @@ func (p *playerController) TokenAuth(c *gin.Context) {
c.Abort()
}
// 以下是内部私有函数
func (p *playerController) maybeCreateNewPlayer(req smsCaptchaReq) (*models.Player, error) {
extAuthID := req.extAuthID()
if Conf.General.ServerEnv == SERVER_ENV_TEST {
@@ -490,7 +480,7 @@ func (p *playerController) maybeCreateNewPlayer(req smsCaptchaReq) (*models.Play
Logger.Info("Got a test env player:", zap.Any("phonenum", req.Num), zap.Any("playerId", player.Id))
return player, nil
}
} else { //正式环境检查是否为bot用户
} else {
botPlayer, err := models.GetPlayerByName(req.Num)
if err != nil {
Logger.Error("Seeking bot player error:", zap.Error(err))
@@ -535,19 +525,17 @@ func (p *playerController) maybeCreatePlayerWechatAuthBinding(userInfo utils.Use
return nil, err
}
if player != nil {
{ //更新玩家姓名及头像
updateInfo := models.Player{
Avatar: userInfo.HeadImgURL,
DisplayName: userInfo.Nickname,
}
tx := storage.MySQLManagerIns.MustBegin()
defer tx.Rollback()
ok, err := models.Update(tx, player.Id, &updateInfo)
if err != nil && ok != true {
return nil, err
} else {
tx.Commit()
}
updateInfo := models.Player{
Avatar: userInfo.HeadImgURL,
DisplayName: userInfo.Nickname,
}
tx := storage.MySQLManagerIns.MustBegin()
defer tx.Rollback()
ok, err := models.Update(tx, player.Id, &updateInfo)
if err != nil && ok != true {
return nil, err
} else {
tx.Commit()
}
return player, nil
}
@@ -573,19 +561,17 @@ func (p *playerController) maybeCreatePlayerWechatGameAuthBinding(userInfo utils
return nil, err
}
if player != nil {
{ //更新玩家姓名及头像
updateInfo := models.Player{
Avatar: userInfo.HeadImgURL,
DisplayName: userInfo.Nickname,
}
tx := storage.MySQLManagerIns.MustBegin()
defer tx.Rollback()
ok, err := models.Update(tx, player.Id, &updateInfo)
if err != nil && ok != true {
return nil, err
} else {
tx.Commit()
}
updateInfo := models.Player{
Avatar: userInfo.HeadImgURL,
DisplayName: userInfo.Nickname,
}
tx := storage.MySQLManagerIns.MustBegin()
defer tx.Rollback()
ok, err := models.Update(tx, player.Id, &updateInfo)
if err != nil && ok != true {
return nil, err
} else {
tx.Commit()
}
return player, nil
}
@@ -670,15 +656,13 @@ func sendSMSViaVendor(mobile string, nationcode string, captchaCode string) int
Nationcode: nationcode,
}
var captchaExpireMin string
//短信有效期hardcode
if Conf.General.ServerEnv == SERVER_ENV_TEST {
//测试环境下有效期为20秒 先hardcode
captchaExpireMin = "0.5"
captchaExpireMin = "0.5" // Hardcoded
} else {
captchaExpireMin = strconv.Itoa(int(ConstVals.Player.CaptchaExpire) / 60000000000)
}
params := [2]string{captchaCode, captchaExpireMin}
appkey := "41a5142feff0b38ade02ea12deee9741" // TODO: Should read from config file!
appkey := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // TODO: Should read from config file!
rand := strconv.Itoa(utils.Rand.Number(1000, 9999))
now := utils.UnixtimeSec()
@@ -692,7 +676,7 @@ func sendSMSViaVendor(mobile string, nationcode string, captchaCode string) int
Extend: "",
Params: &params,
Sig: sig,
Sign: "洛克互娱",
Sign: "YYYYYYYYYYYYYYYYY",
Tel: tel,
Time: now,
Tpl_id: 207399,
@@ -703,7 +687,7 @@ func sendSMSViaVendor(mobile string, nationcode string, captchaCode string) int
Logger.Info("json marshal", zap.Any("err:", err))
return -1
}
resp, err := http.Post("https://yun.tim.qq.com/v5/tlssmssvr/sendsms?sdkappid=1400150185&random="+rand,
resp, err := http.Post("https://yun.tim.qq.com/v5/tlssmssvr/sendsms?sdkappid=uuuuuuuuuuuuuuuuuuuuuuuu&random="+rand,
"application/json",
req)
if err != nil {

View File

@@ -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 {

View File

@@ -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()

View File

@@ -1,8 +1,11 @@
package utils
import (
. "battle_srv/common"
. "battle_srv/configs"
"bytes"
"crypto/sha1"
. "dnmshared"
"encoding/json"
"fmt"
"go.uber.org/zap"
@@ -10,8 +13,6 @@ import (
"io/ioutil"
"math/rand"
"net/http"
. "server/common"
. "server/configs"
"sort"
"time"
)
@@ -94,7 +95,7 @@ func (w *wechat) GetJsConfig(uri string) (config *JsConfig, err error) {
return
}
//TODO add cache, getTicket 获取jsapi_ticket
// TODO add cache, getTicket 获取jsapi_ticket
func (w *wechat) getTicket() (ticketStr string, err error) {
var ticket resTicket
ticket, err = w.getTicketFromServer()
@@ -131,7 +132,7 @@ func (w *wechat) GetOauth2Basic(authcode string) (result resAccessToken, err err
return
}
//UserInfo 用户授权获取到用户信息
// UserInfo 用户授权获取到用户信息
type UserInfo struct {
CommonError
OpenID string `json:"openid"`
@@ -164,7 +165,7 @@ func (w *wechat) GetMoreInfo(accessToken string, openId string) (result UserInfo
return
}
//HTTPGet get 请求
// HTTPGet get 请求
func get(uri string) ([]byte, error) {
response, err := http.Get(uri)
if err != nil {
@@ -182,7 +183,7 @@ func get(uri string) ([]byte, error) {
return body, err
}
//PostJSON post json 数据请求
// PostJSON post json 数据请求
func post(uri string, obj interface{}) ([]byte, error) {
jsonData, err := json.Marshal(obj)
if err != nil {
@@ -206,7 +207,7 @@ func post(uri string, obj interface{}) ([]byte, error) {
return ioutil.ReadAll(response.Body)
}
//Signature sha1签名
// Signature sha1签名
func signature(params ...string) string {
sort.Strings(params)
h := sha1.New()
@@ -216,7 +217,7 @@ func signature(params ...string) string {
return fmt.Sprintf("%x", h.Sum(nil))
}
//RandomStr 随机生成字符串
// RandomStr 随机生成字符串
func randomStr(length int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
bytes := []byte(str)
@@ -228,7 +229,7 @@ func randomStr(length int) string {
return string(result)
}
//getTicketFromServer 强制从服务器中获取ticket
// getTicketFromServer 强制从服务器中获取ticket
func (w *wechat) getTicketFromServer() (ticket resTicket, err error) {
var accessToken string
accessToken, err = w.getAccessTokenFromServer()
@@ -249,14 +250,10 @@ func (w *wechat) getTicketFromServer() (ticket resTicket, err error) {
return
}
//jsAPITicketCacheKey := fmt.Sprintf("jsapi_ticket_%s", w.config.AppID)
//expires := ticket.ExpiresIn - 1500
//set
//err = js.Cache.Set(jsAPITicketCacheKey, ticket.Ticket, time.Duration(expires)*time.Second)
return
}
//GetAccessTokenFromServer 强制从微信服务器获取token
// GetAccessTokenFromServer 强制从微信服务器获取token
func (w *wechat) getAccessTokenFromServer() (accessToken string, err error) {
AccessTokenURL := w.config.ApiProtocol + "://" + w.config.ApiGateway + "/cgi-bin/token"
url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, w.config.AppID, w.config.AppSecret)
@@ -275,9 +272,6 @@ func (w *wechat) getAccessTokenFromServer() (accessToken string, err error) {
return
}
//accessTokenCacheKey := fmt.Sprintf("access_token_%s", w.config.AppID)
//expires := r.ExpiresIn - 1500
//set to redis err = ctx.Cache.Set(accessTokenCacheKey, r.AccessToken, time.Duration(expires)*time.Second)
accessToken = r.AccessToken
return
}

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}$`)
)
// 隐式导入
var ConstVals = &struct {
Player struct {
CaptchaExpire time.Duration

View File

@@ -1,21 +1,24 @@
package env_tools
import (
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/models"
"battle_srv/storage"
. "dnmshared"
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"go.uber.org/zap"
. "server/common"
"server/common/utils"
"server/models"
"server/storage"
)
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 {
@@ -66,23 +71,29 @@ func createMysqlData(rows *sqlx.Rows, v string) {
}
}
//加上tableName参数, 用于pre_conf_data.sqlite里bot_player表的复用 --kobako
// 加上tableName参数, 用于pre_conf_data.sqlite里bot_player表的复用 --kobako
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

View File

@@ -1,10 +1,11 @@
package env_tools
import (
. "server/common"
"server/common/utils"
"server/models"
"server/storage"
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/models"
"battle_srv/storage"
. "dnmshared"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
@@ -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 {

View File

@@ -1,37 +1,51 @@
module server
module battle_srv
go 1.19
require (
github.com/ByteArena/box2d v1.0.2
github.com/ChimeraCoder/gojson v1.0.0 // indirect
github.com/Masterminds/squirrel v0.0.0-20180815162352-8a7e65843414
github.com/davecgh/go-spew v1.1.1
github.com/fatih/color v1.7.0 // indirect
github.com/gin-contrib/cors v0.0.0-20180514151808-6f0a820f94be
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
github.com/gin-gonic/gin v1.3.0
github.com/githubnemo/CompileDaemon v1.0.0 // indirect
github.com/go-redis/redis v6.13.2+incompatible
github.com/go-sql-driver/mysql v1.4.0
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.9 // indirect
github.com/gorilla/websocket v1.2.0
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186
github.com/howeyc/fsnotify v0.9.0 // indirect
github.com/imdario/mergo v0.3.6
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
github.com/solarlune/resolv v0.5.1
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 (
github.com/ChimeraCoder/gojson v1.0.0 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
github.com/githubnemo/CompileDaemon v1.0.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/howeyc/fsnotify v0.9.0 // indirect
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.3 // indirect
github.com/mattn/go-sqlite3 v1.9.0
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect
github.com/ugorji/go v1.1.1 // indirect
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1
google.golang.org/protobuf v1.28.1
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.1 // indirect
)
replace (
dnmshared => ../dnmshared
)

View File

@@ -45,6 +45,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE=
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
@@ -55,12 +57,18 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE=
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
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/thoas/go-funk v0.0.0-20180716193722-1060394a7713 h1:knaxjm6QMbUMNvuaSnJZmw0gRX4V/79JVUQiziJGM84=
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713/go.mod h1:mlR+dHGb+4YgXkf13rkQTuzrneeHANxOm6+ZnEV9HsA=
github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w=
@@ -71,6 +79,8 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=

View File

@@ -1,24 +1,24 @@
package main
import (
"battle_srv/api"
"battle_srv/api/v1"
. "battle_srv/common"
"battle_srv/env_tools"
"battle_srv/models"
"battle_srv/storage"
"battle_srv/ws"
"context"
"fmt"
"net/http"
"os"
"os/signal"
"path/filepath"
"server/api"
"server/api/v1"
. "server/common"
"server/common/utils"
"server/configs"
"server/env_tools"
"server/models"
"server/storage"
"server/ws"
"syscall"
"time"
. "dnmshared"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/robfig/cron"
@@ -30,8 +30,6 @@ func main() {
MustParseConstants()
storage.Init()
env_tools.LoadPreConf()
utils.InitWechat(configs.WechatConfigIns)
utils.InitWechatGame(configs.WechatGameConfigIns)
if Conf.General.ServerEnv == SERVER_ENV_TEST {
env_tools.MergeTestPlayerAccounts()
}

View File

@@ -1,13 +1,9 @@
package models
import (
"github.com/ByteArena/box2d"
. "dnmshared/sharedprotos"
)
type Barrier struct {
X float64
Y float64
Type uint32
Boundary *Polygon2D
CollidableBody *box2d.B2Body
Boundary *Polygon2D
}

View File

@@ -1,19 +0,0 @@
package models
import (
"github.com/ByteArena/box2d"
)
type Bullet struct {
LocalIdInBattle int32 `json:"-"`
LinearSpeed float64 `json:"-"`
X float64 `json:"-"`
Y float64 `json:"-"`
Removed bool `json:"-"`
Dir *Direction `json:"-"`
StartAtPoint *Vec2D `json:"-"`
EndAtPoint *Vec2D `json:"-"`
DamageBoundary *Polygon2D `json:"-"`
CollidableBody *box2d.B2Body `json:"-"`
RemovedAtFrameId int32 `json:"-"`
}

View File

@@ -86,7 +86,7 @@ func (p *InRangePlayerCollection) NextPlayerToAttack() *InRangePlayerNode {
//TODO: 完成重构
/// Doubly circular linked list Implement
// / Doubly circular linked list Implement
type InRangePlayerNode struct {
Prev *InRangePlayerNode
Next *InRangePlayerNode

View File

@@ -1,132 +0,0 @@
package models
import (
"fmt"
"github.com/ByteArena/box2d"
"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"`
}
func CreateVec2DFromB2Vec2(b2V2 box2d.B2Vec2) *Vec2D {
return &Vec2D{
X: b2V2.X,
Y: b2V2.Y,
}
}
func (v2 *Vec2D) ToB2Vec2() box2d.B2Vec2 {
return box2d.MakeB2Vec2(v2.X, v2.Y)
}
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 MoveDynamicBody(body *box2d.B2Body, pToTargetPos *box2d.B2Vec2, inSeconds float64) {
if body.GetType() != box2d.B2BodyType.B2_dynamicBody {
return
}
body.SetTransform(*pToTargetPos, 0.0)
body.SetLinearVelocity(box2d.MakeB2Vec2(0.0, 0.0))
body.SetAngularVelocity(0.0)
}
func PrettyPrintFixture(fix *box2d.B2Fixture) {
fmt.Printf("\t\tfriction:\t%v\n", fix.M_friction)
fmt.Printf("\t\trestitution:\t%v\n", fix.M_restitution)
fmt.Printf("\t\tdensity:\t%v\n", fix.M_density)
fmt.Printf("\t\tisSensor:\t%v\n", fix.M_isSensor)
fmt.Printf("\t\tfilter.categoryBits:\t%d\n", fix.M_filter.CategoryBits)
fmt.Printf("\t\tfilter.maskBits:\t%d\n", fix.M_filter.MaskBits)
fmt.Printf("\t\tfilter.groupIndex:\t%d\n", fix.M_filter.GroupIndex)
switch fix.M_shape.GetType() {
case box2d.B2Shape_Type.E_circle:
{
s := fix.M_shape.(*box2d.B2CircleShape)
fmt.Printf("\t\tb2CircleShape shape: {\n")
fmt.Printf("\t\t\tradius:\t%v\n", s.M_radius)
fmt.Printf("\t\t\toffset:\t%v\n", s.M_p)
fmt.Printf("\t\t}\n")
}
break
case box2d.B2Shape_Type.E_polygon:
{
s := fix.M_shape.(*box2d.B2PolygonShape)
fmt.Printf("\t\tb2PolygonShape shape: {\n")
for i := 0; i < s.M_count; i++ {
fmt.Printf("\t\t\t%v\n", s.M_vertices[i])
}
fmt.Printf("\t\t}\n")
}
break
default:
break
}
}
func PrettyPrintBody(body *box2d.B2Body) {
bodyIndex := body.M_islandIndex
fmt.Printf("{\n")
fmt.Printf("\tHeapRAM addr:\t%p\n", body)
fmt.Printf("\ttype:\t%d\n", body.M_type)
fmt.Printf("\tposition:\t%v\n", body.GetPosition())
fmt.Printf("\tangle:\t%v\n", body.M_sweep.A)
fmt.Printf("\tlinearVelocity:\t%v\n", body.GetLinearVelocity())
fmt.Printf("\tangularVelocity:\t%v\n", body.GetAngularVelocity())
fmt.Printf("\tlinearDamping:\t%v\n", body.M_linearDamping)
fmt.Printf("\tangularDamping:\t%v\n", body.M_angularDamping)
fmt.Printf("\tallowSleep:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_autoSleepFlag)
fmt.Printf("\tawake:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_awakeFlag)
fmt.Printf("\tfixedRotation:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_fixedRotationFlag)
fmt.Printf("\tbullet:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_bulletFlag)
fmt.Printf("\tactive:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_activeFlag)
fmt.Printf("\tgravityScale:\t%v\n", body.M_gravityScale)
fmt.Printf("\tislandIndex:\t%v\n", bodyIndex)
fmt.Printf("\tfixtures: {\n")
for f := body.M_fixtureList; f != nil; f = f.M_next {
PrettyPrintFixture(f)
}
fmt.Printf("\t}\n")
fmt.Printf("}\n")
}
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
dx := pt1.X - pt2.X
dy := pt1.Y - pt2.Y
return math.Sqrt(dx*dx + dy*dy)
}

View File

@@ -1,82 +1,22 @@
package models
import (
pb "server/pb_output"
. "battle_srv/protos"
. "dnmshared/sharedprotos"
)
func toPbVec2D(modelInstance *Vec2D) *pb.Vec2D {
toRet := &pb.Vec2D{
X: modelInstance.X,
Y: modelInstance.Y,
}
return toRet
}
func toPbPolygon2D(modelInstance *Polygon2D) *pb.Polygon2D {
toRet := &pb.Polygon2D{
Anchor: toPbVec2D(modelInstance.Anchor),
Points: make([]*pb.Vec2D, len(modelInstance.Points)),
}
for index, p := range modelInstance.Points {
toRet.Points[index] = toPbVec2D(p)
}
return toRet
}
func toPbVec2DList(modelInstance *Vec2DList) *pb.Vec2DList {
toRet := &pb.Vec2DList{
Vec2DList: make([]*pb.Vec2D, len(*modelInstance)),
}
for k, v := range *modelInstance {
toRet.Vec2DList[k] = toPbVec2D(v)
}
return toRet
}
func toPbPolygon2DList(modelInstance *Polygon2DList) *pb.Polygon2DList {
toRet := &pb.Polygon2DList{
Polygon2DList: make([]*pb.Polygon2D, len(*modelInstance)),
}
for k, v := range *modelInstance {
toRet.Polygon2DList[k] = toPbPolygon2D(v)
}
return toRet
}
func ToPbStrToBattleColliderInfo(intervalToPing int32, willKickIfInactiveFor int32, boundRoomId int32, stageName string, modelInstance1 StrToVec2DListMap, modelInstance2 StrToPolygon2DListMap, stageDiscreteW int32, stageDiscreteH int32, stageTileW int32, stageTileH int32) *pb.BattleColliderInfo {
toRet := &pb.BattleColliderInfo{
IntervalToPing: intervalToPing,
WillKickIfInactiveFor: willKickIfInactiveFor,
BoundRoomId: boundRoomId,
StageName: stageName,
StrToVec2DListMap: make(map[string]*pb.Vec2DList, 0),
StrToPolygon2DListMap: make(map[string]*pb.Polygon2DList, 0),
StageDiscreteW: stageDiscreteW,
StageDiscreteH: stageDiscreteH,
StageTileW: stageTileW,
StageTileH: stageTileH,
}
for k, v := range modelInstance1 {
toRet.StrToVec2DListMap[k] = toPbVec2DList(v)
}
for k, v := range modelInstance2 {
toRet.StrToPolygon2DListMap[k] = toPbPolygon2DList(v)
}
return toRet
}
func toPbPlayers(modelInstances map[int32]*Player) map[int32]*pb.Player {
toRet := make(map[int32]*pb.Player, 0)
func toPbPlayers(modelInstances map[int32]*Player) map[int32]*PlayerDownsync {
toRet := make(map[int32]*PlayerDownsync, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &pb.Player{
Id: last.Id,
X: last.X,
Y: last.Y,
Dir: &pb.Direction{
toRet[k] = &PlayerDownsync{
Id: last.Id,
VirtualGridX: last.VirtualGridX,
VirtualGridY: last.VirtualGridY,
Dir: &Direction{
Dx: last.Dir.Dx,
Dy: last.Dir.Dy,
},
@@ -90,114 +30,3 @@ func toPbPlayers(modelInstances map[int32]*Player) map[int32]*pb.Player {
return toRet
}
func toPbTreasures(modelInstances map[int32]*Treasure) map[int32]*pb.Treasure {
toRet := make(map[int32]*pb.Treasure, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &pb.Treasure{
Id: last.Id,
LocalIdInBattle: last.LocalIdInBattle,
Score: last.Score,
X: last.X,
Y: last.Y,
Removed: last.Removed,
Type: last.Type,
}
}
return toRet
}
func toPbTraps(modelInstances map[int32]*Trap) map[int32]*pb.Trap {
toRet := make(map[int32]*pb.Trap, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &pb.Trap{
Id: last.Id,
LocalIdInBattle: last.LocalIdInBattle,
X: last.X,
Y: last.Y,
Removed: last.Removed,
Type: last.Type,
}
}
return toRet
}
func toPbBullets(modelInstances map[int32]*Bullet) map[int32]*pb.Bullet {
toRet := make(map[int32]*pb.Bullet, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
if nil == last.StartAtPoint || nil == last.EndAtPoint {
continue
}
toRet[k] = &pb.Bullet{
LocalIdInBattle: last.LocalIdInBattle,
LinearSpeed: last.LinearSpeed,
X: last.X,
Y: last.Y,
Removed: last.Removed,
StartAtPoint: &pb.Vec2D{
X: last.StartAtPoint.X,
Y: last.StartAtPoint.Y,
},
EndAtPoint: &pb.Vec2D{
X: last.EndAtPoint.X,
Y: last.EndAtPoint.Y,
},
}
}
return toRet
}
func toPbSpeedShoes(modelInstances map[int32]*SpeedShoe) map[int32]*pb.SpeedShoe {
toRet := make(map[int32]*pb.SpeedShoe, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &pb.SpeedShoe{
Id: last.Id,
LocalIdInBattle: last.LocalIdInBattle,
X: last.X,
Y: last.Y,
Removed: last.Removed,
Type: last.Type,
}
}
return toRet
}
func toPbGuardTowers(modelInstances map[int32]*GuardTower) map[int32]*pb.GuardTower {
toRet := make(map[int32]*pb.GuardTower, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &pb.GuardTower{
Id: last.Id,
LocalIdInBattle: last.LocalIdInBattle,
X: last.X,
Y: last.Y,
Removed: last.Removed,
Type: last.Type,
}
}
return toRet
}

View File

@@ -2,8 +2,8 @@ package models
import (
"database/sql"
. "dnmshared/sharedprotos"
"fmt"
"github.com/ByteArena/box2d"
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
)
@@ -33,31 +33,32 @@ func InitPlayerBattleStateIns() {
}
type Player struct {
Id int32 `json:"id,omitempty" db:"id"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Dir *Direction `json:"dir,omitempty"`
Speed int32 `json:"speed,omitempty"`
BattleState int32 `json:"battleState,omitempty"`
LastMoveGmtMillis int32 `json:"lastMoveGmtMillis,omitempty"`
Score int32 `json:"score,omitempty"`
Removed bool `json:"removed,omitempty"`
JoinIndex int32
// Meta info fields
Id int32 `json:"id,omitempty" db:"id"`
Name string `json:"name,omitempty" db:"name"`
DisplayName string `json:"displayName,omitempty" db:"display_name"`
Avatar string `json:"avatar,omitempty"`
ColliderRadius float64 `json:"-"`
Name string `json:"name,omitempty" db:"name"`
DisplayName string `json:"displayName,omitempty" db:"display_name"`
Avatar string `json:"avatar,omitempty"`
// DB only fields
CreatedAt int64 `db:"created_at"`
UpdatedAt int64 `db:"updated_at"`
DeletedAt NullInt64 `db:"deleted_at"`
TutorialStage int `db:"tutorial_stage"`
FrozenAtGmtMillis int64 `json:"-" db:"-"`
AddSpeedAtGmtMillis int64 `json:"-" db:"-"`
CreatedAt int64 `json:"-" db:"created_at"`
UpdatedAt int64 `json:"-" db:"updated_at"`
DeletedAt NullInt64 `json:"-" db:"deleted_at"`
TutorialStage int `json:"-" db:"tutorial_stage"`
CollidableBody *box2d.B2Body `json:"-"`
AckingFrameId int32 `json:"ackingFrameId"`
AckingInputFrameId int32 `json:"-"`
LastSentInputFrameId int32 `json:"-"`
// in-battle info fields
VirtualGridX int32
VirtualGridY int32
Dir *Direction
Speed int32
BattleState int32
LastMoveGmtMillis int32
Score int32
Removed bool
JoinIndex int32
AckingFrameId int32
AckingInputFrameId int32
LastSentInputFrameId int32
}
func ExistPlayerByName(name string) (bool, error) {

View File

@@ -1,9 +1,9 @@
package models
import (
"battle_srv/storage"
"database/sql"
. "server/common"
"server/storage"
. "dnmshared"
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"

View File

@@ -1,10 +1,10 @@
package models
import (
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/storage"
"database/sql"
. "server/common"
"server/common/utils"
"server/storage"
sq "github.com/Masterminds/squirrel"
)

View File

@@ -1,10 +1,11 @@
package models
import (
. "battle_srv/common"
"battle_srv/common/utils"
"database/sql"
. "dnmshared"
"errors"
. "server/common"
"server/common/utils"
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"

View File

@@ -1,14 +0,0 @@
package models
import "github.com/ByteArena/box2d"
type Pumpkin struct {
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
LinearSpeed float64 `json:"linearSpeed,omitempty"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Removed bool `json:"removed,omitempty"`
Dir *Direction `json:"-"`
CollidableBody *box2d.B2Body `json:"-"`
RemovedAtFrameId int32 `json:"-"`
}

View File

@@ -1,8 +1,8 @@
package models
type RingBuffer struct {
Ed int32 // write index
St int32 // read index
Ed int32 // write index, open index
St int32 // read index, closed index
EdFrameId int32
StFrameId int32
N int32

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,9 @@ package models
import (
"container/heap"
. "dnmshared"
"fmt"
"github.com/gorilla/websocket"
"go.uber.org/zap"
. "server/common"
"sync"
)
@@ -92,43 +91,13 @@ func InitRoomHeapManager() {
for i := 0; i < initialCountOfRooms; i++ {
roomCapacity := 2
joinIndexBooleanArr := make([]bool, roomCapacity)
for index, _ := range joinIndexBooleanArr {
joinIndexBooleanArr[index] = false
}
currentRoomBattleState := RoomBattleStateIns.IDLE
pq[i] = &Room{
Id: int32(i + 1),
Players: make(map[int32]*Player),
PlayerDownsyncSessionDict: make(map[int32]*websocket.Conn),
PlayerSignalToCloseDict: make(map[int32]SignalToCloseConnCbType),
Capacity: roomCapacity,
Score: calRoomScore(0, roomCapacity, currentRoomBattleState),
State: currentRoomBattleState,
Index: i,
Tick: 0,
EffectivePlayerCount: 0,
//BattleDurationNanos: int64(5 * 1000 * 1000 * 1000),
BattleDurationNanos: int64(30 * 1000 * 1000 * 1000),
ServerFPS: 60,
Treasures: make(map[int32]*Treasure),
Traps: make(map[int32]*Trap),
GuardTowers: make(map[int32]*GuardTower),
Bullets: make(map[int32]*Bullet),
SpeedShoes: make(map[int32]*SpeedShoe),
Barriers: make(map[int32]*Barrier),
Pumpkins: make(map[int32]*Pumpkin),
AccumulatedLocalIdForBullets: 0,
AllPlayerInputsBuffer: NewRingBuffer(1024),
LastAllConfirmedInputFrameId: -1,
LastAllConfirmedInputFrameIdWithChange: -1,
LastAllConfirmedInputList: make([]uint64, roomCapacity),
InputDelayFrames: 4,
InputScaleFrames: 2,
JoinIndexBooleanArr: joinIndexBooleanArr,
Id: int32(i + 1),
Capacity: roomCapacity,
Index: i,
}
roomMap[pq[i].Id] = pq[i]
pq[i].ChooseStage()
pq[i].OnDismissed()
}
heap.Init(&pq)
RoomHeapManagerIns = &pq

View File

@@ -1,17 +0,0 @@
package models
import (
"github.com/ByteArena/box2d"
)
type SpeedShoe struct {
Id int32 `json:"id,omitempty"`
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Removed bool `json:"removed,omitempty"`
Type int32 `json:"type,omitempty"`
PickupBoundary *Polygon2D `json:"-"`
CollidableBody *box2d.B2Body `json:"-"`
RemovedAtFrameId int32 `json:"-"`
}

View File

@@ -1,39 +0,0 @@
package models
import (
"github.com/ByteArena/box2d"
)
type Trap struct {
Id int32 `json:"id,omitempty"`
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
Type int32 `json:"type,omitempty"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Removed bool `json:"removed,omitempty"`
PickupBoundary *Polygon2D `json:"-"`
TrapBullets []*Bullet `json:"-"`
CollidableBody *box2d.B2Body `json:"-"`
RemovedAtFrameId int32 `json:"-"`
}
type GuardTower struct {
Id int32 `json:"id,omitempty"`
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
Type int32 `json:"type,omitempty"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Removed bool `json:"removed,omitempty"`
PickupBoundary *Polygon2D `json:"-"`
TrapBullets []*Bullet `json:"-"`
CollidableBody *box2d.B2Body `json:"-"`
RemovedAtFrameId int32 `json:"-"`
InRangePlayers *InRangePlayerCollection `json:"-"`
LastAttackTick int64 `json:"-"`
TileWidth float64 `json:"-"`
TileHeight float64 `json:"-"`
WidthInB2World float64 `json:"-"`
HeightInB2World float64 `json:"-"`
}

View File

@@ -1,18 +0,0 @@
package models
import (
"github.com/ByteArena/box2d"
)
type Treasure struct {
Id int32 `json:"id,omitempty"`
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
Score int32 `json:"score,omitempty"`
X float64 `json:"x,omitempty"`
Y float64 `json:"y,omitempty"`
Removed bool `json:"removed,omitempty"`
Type int32 `json:"type,omitempty"`
PickupBoundary *Polygon2D `json:"-"`
CollidableBody *box2d.B2Body `json:"-"`
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,13 @@
package storage
import (
. "server/common"
. "battle_srv/common"
_ "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))
}

View File

@@ -1,12 +1,14 @@
package storage
import (
. "battle_srv/common"
"fmt"
. "server/common"
"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))
}

View File

@@ -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{}

View File

@@ -1,6 +1,9 @@
package ws
import (
. "battle_srv/common"
"battle_srv/models"
pb "battle_srv/protos"
"container/heap"
"fmt"
"github.com/gin-gonic/gin"
@@ -8,12 +11,12 @@ import (
"github.com/gorilla/websocket"
"go.uber.org/zap"
"net/http"
. "server/common"
"server/models"
pb "server/pb_output"
"strconv"
"sync/atomic"
"time"
. "dnmshared"
"runtime/debug"
)
const (
@@ -102,7 +105,7 @@ func Serve(c *gin.Context) {
}
defer func() {
if r := recover(); r != nil {
Logger.Warn("Recovered from: ", zap.Any("panic", r))
Logger.Error("Recovered from: ", zap.Any("panic", r))
}
}()
/**
@@ -240,16 +243,42 @@ func Serve(c *gin.Context) {
})
}()
playerBattleColliderInfo := models.ToPbStrToBattleColliderInfo(int32(Constants.Ws.IntervalToPing), int32(Constants.Ws.WillKickIfInactiveFor), pRoom.Id, pRoom.StageName, pRoom.RawBattleStrToVec2DListMap, pRoom.RawBattleStrToPolygon2DListMap, pRoom.StageDiscreteW, pRoom.StageDiscreteH, pRoom.StageTileW, pRoom.StageTileH)
// Construct "battleColliderInfo" to downsync
bciFrame := &pb.BattleColliderInfo{
BoundRoomId: pRoom.Id,
StageName: pRoom.StageName,
StrToVec2DListMap: pRoom.RawBattleStrToVec2DListMap,
StrToPolygon2DListMap: pRoom.RawBattleStrToPolygon2DListMap,
StageDiscreteW: pRoom.StageDiscreteW,
StageDiscreteH: pRoom.StageDiscreteH,
StageTileW: pRoom.StageTileW,
StageTileH: pRoom.StageTileH,
IntervalToPing: int32(Constants.Ws.IntervalToPing),
WillKickIfInactiveFor: int32(Constants.Ws.WillKickIfInactiveFor),
BattleDurationNanos: pRoom.BattleDurationNanos,
ServerFps: pRoom.ServerFps,
InputDelayFrames: pRoom.InputDelayFrames,
InputScaleFrames: pRoom.InputScaleFrames,
NstDelayFrames: pRoom.NstDelayFrames,
InputFrameUpsyncDelayTolerance: pRoom.InputFrameUpsyncDelayTolerance,
MaxChasingRenderFramesPerUpdate: pRoom.MaxChasingRenderFramesPerUpdate,
PlayerBattleState: pThePlayer.BattleState, // For frontend to know whether it's rejoining
RollbackEstimatedDtMillis: pRoom.RollbackEstimatedDtMillis,
RollbackEstimatedDtNanos: pRoom.RollbackEstimatedDtNanos,
WorldToVirtualGridRatio: pRoom.WorldToVirtualGridRatio,
VirtualGridToWorldRatio: pRoom.VirtualGridToWorldRatio,
}
resp := &pb.WsResp{
Ret: int32(Constants.RetCode.Ok),
EchoedMsgId: int32(0),
Act: models.DOWNSYNC_MSG_ACT_HB_REQ,
BciFrame: playerBattleColliderInfo,
BciFrame: bciFrame,
}
// Logger.Info("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp))
Logger.Debug("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp))
theBytes, marshalErr := proto.Marshal(resp)
if nil != marshalErr {
@@ -328,7 +357,7 @@ func Serve(c *gin.Context) {
receivingLoopAgainstPlayer := func() error {
defer func() {
if r := recover(); r != nil {
Logger.Warn("Goroutine `receivingLoopAgainstPlayer`, recovery spot#1, recovered from: ", zap.Any("panic", r))
Logger.Error("Goroutine `receivingLoopAgainstPlayer`, recovery spot#1, recovered from: ", zap.Any("panic", r), zap.Any("callstack", debug.Stack()))
}
Logger.Info("Goroutine `receivingLoopAgainstPlayer` is stopped for:", zap.Any("playerId", playerId), zap.Any("roomId", pRoom.Id))
}()

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

File diff suppressed because one or more lines are too long

BIN
charts/InputDelayIntro.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

4
charts/README.md Normal file
View File

@@ -0,0 +1,4 @@
# GIF creation cmd reference
```
ffmpeg -ss 12 -t 13 -i input.mp4 -vf "fps=10,scale=480:-1" -loop 0 output.gif
```

BIN
charts/RollbackAndChase.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
charts/screenshot-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 KiB

View File

@@ -0,0 +1,21 @@
PROJECTNAME=viscol.exe
ROOT_DIR=.
all: help
## Available proxies for downloading go modules are listed in "https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away".
GOPROXY=https://mirrors.aliyun.com/goproxy
build:
go build -o $(ROOT_DIR)/$(PROJECTNAME)
run: build
./$(PROJECTNAME)
.PHONY: help
help: Makefile
@echo
@echo " Choose a command run:"
@echo
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
@echo

View File

@@ -0,0 +1,52 @@
package main
import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/solarlune/resolv"
"image/color"
)
var (
PolygonFillerImage = ebiten.NewImage(1, 1)
)
func DrawPolygon(screen *ebiten.Image, shape *resolv.ConvexPolygon, clr color.Color) {
PolygonFillerImage.Fill(clr)
indices := []uint16{}
vs := []ebiten.Vertex{}
coors := shape.Transformed()
centerX := float64(0)
centerY := float64(0)
n := uint16(len(coors))
for i, coor := range coors {
centerX += coor.X()
centerY += coor.Y()
vs = append(vs, ebiten.Vertex{
DstX: float32(coor.X()),
DstY: float32(coor.Y()),
SrcX: 0,
SrcY: 0,
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
})
indices = append(indices, uint16(i), uint16(i+1)%n, n)
}
centerX = centerX / float64(n)
centerY = centerY / float64(n)
vs = append(vs, ebiten.Vertex{
DstX: float32(centerX),
DstY: float32(centerY),
SrcX: 0,
SrcY: 0,
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
})
screen.DrawTriangles(vs, indices, PolygonFillerImage, nil)
}

Binary file not shown.

View File

@@ -0,0 +1,28 @@
module viscol
go 1.19
require (
dnmshared v0.0.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/hajimehoshi/ebiten/v2 v2.4.7
github.com/solarlune/resolv v0.5.1
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
)
require (
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad // indirect
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 // indirect
github.com/jezek/xgb v1.0.1 // 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/mobile v0.0.0-20220722155234-aaac322e2105 // indirect
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
replace dnmshared => ../dnmshared

102
collider_visualizer/go.sum Normal file
View File

@@ -0,0 +1,102 @@
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/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/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hajimehoshi/bitmapfont/v2 v2.2.1 h1:y7zcy02/UgO24IL3COqYtrRZzhRucNBtmCo/SNU648k=
github.com/hajimehoshi/bitmapfont/v2 v2.2.1/go.mod h1:wjrYAy8vKgj9JsFgnYAOK346/uvE22TlmqouzdnYIs0=
github.com/hajimehoshi/ebiten/v2 v2.4.7 h1:XuvB7R0Rbw/O7g6vNU8gqr5b9e7MNhhAONMSsyreLDI=
github.com/hajimehoshi/ebiten/v2 v2.4.7/go.mod h1:Ofk1EfQZZ8tL0TlEPF5wPrnN+8Oa/ywuQOYh+uYsqLQ=
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 h1:s01qIIRG7vN/5ndLwkDktjx44ulFk6apvAjVBYR50Yo=
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
github.com/jakecoffman/cp v1.2.1/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
github.com/jezek/xgb v1.0.1 h1:YUGhxps0aR7J2Xplbs23OHnV1mWaxFVcOl9b+1RQkt8=
github.com/jezek/xgb v1.0.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/jfreymuth/oggvorbis v1.0.4/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
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/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/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.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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=

172
collider_visualizer/main.go Normal file
View File

@@ -0,0 +1,172 @@
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 {
// stageName := "simple" // Use this for calibration in isometric orientation
// stageName := "richsoil"
stageName := "dungeon"
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
if nil != err {
panic(err)
}
PolygonFillerImage.Fill(color.RGBA{60, 60, 60, 255}) // Required to init color of the polygons!
spaceW := stageDiscreteW * stageTileW
spaceH := stageDiscreteH * stageTileH
ebiten.SetWindowResizable(true)
ebiten.SetWindowTitle("resolv test")
g := &Game{
Width: int(spaceW),
Height: int(spaceH),
ShowHelpText: true,
}
g.World = NewWorldColliderDisplay(g, stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap)
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() {
ebiten.RunGame(NewGame())
}

View File

@@ -0,0 +1,122 @@
package main
import (
. "dnmshared"
. "dnmshared/sharedprotos"
"fmt"
"github.com/hajimehoshi/ebiten/v2"
"github.com/solarlune/resolv"
"go.uber.org/zap"
"image/color"
)
type WorldColliderDisplay struct {
Game *Game
Space *resolv.Space
}
func (world *WorldColliderDisplay) Init() {
}
func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTileW, stageTileH int32, playerPosMap StrToVec2DListMap, barrierMap StrToPolygon2DListMap) *WorldColliderDisplay {
playerPosList := *(playerPosMap["PlayerStartingPos"])
barrierList := *(barrierMap["Barrier"])
world := &WorldColliderDisplay{Game: game}
Logger.Info("Parsed variables", zap.Any("stageDiscreteW", stageDiscreteW), zap.Any("stageDiscreteH", stageDiscreteH), zap.Any("stageTileW", stageTileW), zap.Any("stageTileH", stageTileH))
spaceW := stageDiscreteW * stageTileW
spaceH := stageDiscreteH * stageTileH
spaceOffsetX := float64(spaceW) * 0.5
spaceOffsetY := float64(spaceH) * 0.5
virtualGridToWorldRatio := 0.1
playerDefaultSpeed := 20
minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 2)
playerColliderRadius := float64(16)
playerColliders := make([]*resolv.Object, len(playerPosList.Eles))
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)
for i, playerPos := range playerPosList.Eles {
playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
Logger.Info(fmt.Sprintf("Player Collider#%d: player world pos =(%.2f, %.2f), shape=%v", i, playerPos.X, playerPos.Y, ConvexPolygonStr(playerCollider.Shape.(*resolv.ConvexPolygon))))
playerColliders[i] = playerCollider
space.Add(playerCollider)
}
barrierLocalId := 0
for _, barrierUnaligned := range barrierList.Eles {
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
Logger.Info(fmt.Sprintf("Added barrier: shape=%v", ConvexPolygonStr(barrierCollider.Shape.(*resolv.ConvexPolygon))))
space.Add(barrierCollider)
barrierLocalId++
}
world.Space = space
moveToCollide := false
if moveToCollide {
newVx, newVy := int32(-2959), int32(-2261)
effPushback := Vec2D{X: float64(0), Y: float64(0)}
toTestPlayerCollider := playerColliders[0]
toTestPlayerCollider.X, toTestPlayerCollider.Y = VirtualGridToPolygonColliderAnchorPos(newVx, newVy, playerColliderRadius, playerColliderRadius, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
Logger.Info(fmt.Sprintf("Checking collision for virtual (%d, %d), now playerShape=%v", newVx, newVy, ConvexPolygonStr(toTestPlayerCollider.Shape.(*resolv.ConvexPolygon))))
toTestPlayerCollider.Update()
if collision := toTestPlayerCollider.Check(0, 0); collision != nil {
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
for _, obj := range collision.Objects {
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape); overlapped {
Logger.Warn(fmt.Sprintf("Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
effPushback.X += pushbackX
effPushback.Y += pushbackY
} else {
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), overlapResult))
}
}
toTestPlayerCollider.X -= effPushback.X
toTestPlayerCollider.Y -= effPushback.Y
toTestPlayerCollider.Update()
Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y))
}
}
return world
}
func (world *WorldColliderDisplay) Update() {
}
func (world *WorldColliderDisplay) Draw(screen *ebiten.Image) {
for _, o := range world.Space.Objects() {
if o.HasTags("Player") {
drawColor := color.RGBA{0, 255, 0, 255}
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
} else {
drawColor := color.RGBA{60, 60, 60, 255}
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
}
}
world.Game.DebugDraw(screen, world.Space)
if world.Game.ShowHelpText {
world.Game.DrawText(screen, 16, 16,
"~ Collider Display test ~",
"F1: Toggle Debug View",
"F2: Show / Hide help text",
"R: Restart world",
fmt.Sprintf("%d FPS (frames per second)", int(ebiten.CurrentFPS())),
fmt.Sprintf("%d TPS (ticks per second)", int(ebiten.CurrentTPS())),
)
}
}

View File

@@ -0,0 +1,9 @@
package main
import "github.com/hajimehoshi/ebiten/v2"
type WorldInterface interface {
Init()
Update()
Draw(*ebiten.Image)
}

50
dnmshared/geometry.go Normal file
View File

@@ -0,0 +1,50 @@
package dnmshared
import (
. "dnmshared/sharedprotos"
"math"
)
func NormVec2D(dx, dy float64) Vec2D {
return Vec2D{X: dy, Y: -dx}
}
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
// Transform again to put "anchor" at the top-left point of the bounding box for "resolv"
boundingBoxTL := &Vec2D{
X: math.MaxFloat64,
Y: math.MaxFloat64,
}
for _, p := range input.Points {
if p.X < boundingBoxTL.X {
boundingBoxTL.X = p.X
}
if p.Y < boundingBoxTL.Y {
boundingBoxTL.Y = p.Y
}
}
// Now "input.Anchor" should move to "input.Anchor+boundingBoxTL", thus "boundingBoxTL" is also the value of the negative diff for all "input.Points"
output := &Polygon2D{
Anchor: &Vec2D{
X: input.Anchor.X + boundingBoxTL.X,
Y: input.Anchor.Y + boundingBoxTL.Y,
},
Points: make([]*Vec2D, len(input.Points)),
}
for i, p := range input.Points {
output.Points[i] = &Vec2D{
X: p.X - boundingBoxTL.X,
Y: p.Y - boundingBoxTL.Y,
}
}
return output
}
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
dx := pt1.X - pt2.X
dy := pt1.Y - pt2.Y
return math.Sqrt(dx*dx + dy*dy)
}

3
dnmshared/go.mod Normal file
View File

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

View File

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

255
dnmshared/resolv_helper.go Normal file
View File

@@ -0,0 +1,255 @@
package dnmshared
import (
. "dnmshared/sharedprotos"
"fmt"
"github.com/kvartborg/vector"
"github.com/solarlune/resolv"
"math"
"strings"
)
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
var s []string = make([]string, len(body.Points))
for i, p := range body.Points {
s[i] = fmt.Sprintf("[%.2f, %.2f]", p[0]+body.X, p[1]+body.Y)
}
return fmt.Sprintf("{\n%s\n}", strings.Join(s, ",\n"))
}
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
cx, cy := WorldToPolygonColliderAnchorPos(origX, origY, w*0.5, h*0.5, spaceOffsetX, spaceOffsetY)
collider := resolv.NewObject(cx, cy, w, h, tag)
shape := resolv.NewRectangle(0, 0, w, h)
collider.SetShape(shape)
return collider
}
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
var w, h float64 = 0, 0
shape := resolv.NewConvexPolygon()
for i, pi := range aligned.Points {
for j, pj := range aligned.Points {
if i == j {
continue
}
if math.Abs(pj.X-pi.X) > w {
w = math.Abs(pj.X - pi.X)
}
if math.Abs(pj.Y-pi.Y) > h {
h = math.Abs(pj.Y - pi.Y)
}
}
}
for i := 0; i < len(aligned.Points); i++ {
p := aligned.Points[i]
shape.AddPoints(p.X, p.Y)
}
collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag)
collider.SetShape(shape)
return collider
}
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64, *SatResult) {
origX, origY := playerShape.Position()
defer func() {
playerShape.SetPosition(origX, origY)
}()
playerShape.SetPosition(origX+oldDx, origY+oldDy)
overlapResult := &SatResult{
Overlap: 0,
OverlapX: 0,
OverlapY: 0,
AContainedInB: true,
BContainedInA: true,
Axis: vector.Vector{0, 0},
}
if overlapped := IsPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
return true, pushbackX, pushbackY, overlapResult
} else {
return false, 0, 0, overlapResult
}
}
type SatResult struct {
Overlap float64
OverlapX float64
OverlapY float64
AContainedInB bool
BContainedInA bool
Axis vector.Vector
}
func IsPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
aCnt, bCnt := len(a.Points), len(b.Points)
// Single point case
if 1 == aCnt && 1 == bCnt {
if nil != result {
result.Overlap = 0
}
return a.Points[0].X() == b.Points[0].X() && a.Points[0].Y() == b.Points[0].Y()
}
if 1 < aCnt {
for _, axis := range a.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
if 1 < bCnt {
for _, axis := range b.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
return true
}
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
/*
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
Consider the following example
a: {
anchor: [1337.19 1696.74]
points: [[0 0] [24 0] [24 24] [0 24]]
},
b: {
anchor: [1277.72 1570.56]
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
}
e = (-2.98, 1.49).Unit()
*/
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
for _, p := range a.Points {
dot := (p.X()+a.X)*e.X() + (p.Y()+a.Y)*e.Y()
if aStart > dot {
aStart = dot
}
if aEnd < dot {
aEnd = dot
}
}
for _, p := range b.Points {
dot := (p.X()+b.X)*e.X() + (p.Y()+b.Y)*e.Y()
if bStart > dot {
bStart = dot
}
if bEnd < dot {
bEnd = dot
}
}
if aStart > bEnd || aEnd < bStart {
// Separated by unit vector "e"
return true
}
if nil != result {
result.Axis = e
overlap := float64(0)
if aStart < bStart {
result.AContainedInB = false
if aEnd < bEnd {
overlap = aEnd - bStart
result.BContainedInA = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
} else {
result.BContainedInA = false
if aEnd > bEnd {
overlap = aStart - bEnd
result.AContainedInB = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
}
currentOverlap := result.Overlap
absoluteOverlap := overlap
if overlap < 0 {
absoluteOverlap = -overlap
}
if 0 == currentOverlap || currentOverlap > absoluteOverlap {
var sign float64 = 1
if overlap < 0 {
sign = -1
}
result.Overlap = absoluteOverlap
result.OverlapX = e.X() * sign
result.OverlapY = e.Y() * sign
}
}
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
return false
}
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
// [WARNING] Introduces loss of precision!
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
var virtualGridX int32 = int32(math.Round(wx * worldToVirtualGridRatio))
var virtualGridY int32 = int32(math.Round(wy * worldToVirtualGridRatio))
return virtualGridX, virtualGridY
}
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
// No loss of precision
var wx float64 = float64(vx) * virtualGridToWorldRatio
var wy float64 = float64(vy) * virtualGridToWorldRatio
return wx, wy
}
func WorldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return wx - halfBoundingW + collisionSpaceOffsetX, wy - halfBoundingH + collisionSpaceOffsetY
}
func PolygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return cx + halfBoundingW - collisionSpaceOffsetX, cy + halfBoundingH - collisionSpaceOffsetY
}
func PolygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
wx, wy := PolygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
}
func VirtualGridToPolygonColliderAnchorPos(vx, vy int32, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
return WorldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
}

View File

@@ -0,0 +1,427 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.4
// source: geometry.proto
package sharedprotos
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Direction struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Dx int32 `protobuf:"varint,1,opt,name=dx,proto3" json:"dx,omitempty"`
Dy int32 `protobuf:"varint,2,opt,name=dy,proto3" json:"dy,omitempty"`
}
func (x *Direction) Reset() {
*x = Direction{}
if protoimpl.UnsafeEnabled {
mi := &file_geometry_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Direction) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Direction) ProtoMessage() {}
func (x *Direction) ProtoReflect() protoreflect.Message {
mi := &file_geometry_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Direction.ProtoReflect.Descriptor instead.
func (*Direction) Descriptor() ([]byte, []int) {
return file_geometry_proto_rawDescGZIP(), []int{0}
}
func (x *Direction) GetDx() int32 {
if x != nil {
return x.Dx
}
return 0
}
func (x *Direction) GetDy() int32 {
if x != nil {
return x.Dy
}
return 0
}
type Vec2D struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
X float64 `protobuf:"fixed64,1,opt,name=x,proto3" json:"x,omitempty"`
Y float64 `protobuf:"fixed64,2,opt,name=y,proto3" json:"y,omitempty"`
}
func (x *Vec2D) Reset() {
*x = Vec2D{}
if protoimpl.UnsafeEnabled {
mi := &file_geometry_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Vec2D) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Vec2D) ProtoMessage() {}
func (x *Vec2D) ProtoReflect() protoreflect.Message {
mi := &file_geometry_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Vec2D.ProtoReflect.Descriptor instead.
func (*Vec2D) Descriptor() ([]byte, []int) {
return file_geometry_proto_rawDescGZIP(), []int{1}
}
func (x *Vec2D) GetX() float64 {
if x != nil {
return x.X
}
return 0
}
func (x *Vec2D) GetY() float64 {
if x != nil {
return x.Y
}
return 0
}
type Polygon2D struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Anchor *Vec2D `protobuf:"bytes,1,opt,name=anchor,proto3" json:"anchor,omitempty"`
Points []*Vec2D `protobuf:"bytes,2,rep,name=points,proto3" json:"points,omitempty"`
}
func (x *Polygon2D) Reset() {
*x = Polygon2D{}
if protoimpl.UnsafeEnabled {
mi := &file_geometry_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Polygon2D) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Polygon2D) ProtoMessage() {}
func (x *Polygon2D) ProtoReflect() protoreflect.Message {
mi := &file_geometry_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Polygon2D.ProtoReflect.Descriptor instead.
func (*Polygon2D) Descriptor() ([]byte, []int) {
return file_geometry_proto_rawDescGZIP(), []int{2}
}
func (x *Polygon2D) GetAnchor() *Vec2D {
if x != nil {
return x.Anchor
}
return nil
}
func (x *Polygon2D) GetPoints() []*Vec2D {
if x != nil {
return x.Points
}
return nil
}
type Vec2DList struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Eles []*Vec2D `protobuf:"bytes,1,rep,name=eles,proto3" json:"eles,omitempty"`
}
func (x *Vec2DList) Reset() {
*x = Vec2DList{}
if protoimpl.UnsafeEnabled {
mi := &file_geometry_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Vec2DList) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Vec2DList) ProtoMessage() {}
func (x *Vec2DList) ProtoReflect() protoreflect.Message {
mi := &file_geometry_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Vec2DList.ProtoReflect.Descriptor instead.
func (*Vec2DList) Descriptor() ([]byte, []int) {
return file_geometry_proto_rawDescGZIP(), []int{3}
}
func (x *Vec2DList) GetEles() []*Vec2D {
if x != nil {
return x.Eles
}
return nil
}
type Polygon2DList struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Eles []*Polygon2D `protobuf:"bytes,1,rep,name=eles,proto3" json:"eles,omitempty"`
}
func (x *Polygon2DList) Reset() {
*x = Polygon2DList{}
if protoimpl.UnsafeEnabled {
mi := &file_geometry_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Polygon2DList) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Polygon2DList) ProtoMessage() {}
func (x *Polygon2DList) ProtoReflect() protoreflect.Message {
mi := &file_geometry_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Polygon2DList.ProtoReflect.Descriptor instead.
func (*Polygon2DList) Descriptor() ([]byte, []int) {
return file_geometry_proto_rawDescGZIP(), []int{4}
}
func (x *Polygon2DList) GetEles() []*Polygon2D {
if x != nil {
return x.Eles
}
return nil
}
var File_geometry_proto protoreflect.FileDescriptor
var file_geometry_proto_rawDesc = []byte{
0x0a, 0x0e, 0x67, 0x65, 0x6f, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x2b,
0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x64,
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x64, 0x78, 0x12, 0x0e, 0x0a, 0x02, 0x64,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x64, 0x79, 0x22, 0x23, 0x0a, 0x05, 0x56,
0x65, 0x63, 0x32, 0x44, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52,
0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79,
0x22, 0x65, 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x12, 0x2b, 0x0a,
0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x65, 0x63,
0x32, 0x44, 0x52, 0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x68, 0x61,
0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52,
0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x34, 0x0a, 0x09, 0x56, 0x65, 0x63, 0x32, 0x44,
0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x73, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x22, 0x3c, 0x0a,
0x0d, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b,
0x0a, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73,
0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x6f, 0x6c, 0x79,
0x67, 0x6f, 0x6e, 0x32, 0x44, 0x52, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x42, 0x18, 0x5a, 0x16, 0x64,
0x6e, 0x6d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_geometry_proto_rawDescOnce sync.Once
file_geometry_proto_rawDescData = file_geometry_proto_rawDesc
)
func file_geometry_proto_rawDescGZIP() []byte {
file_geometry_proto_rawDescOnce.Do(func() {
file_geometry_proto_rawDescData = protoimpl.X.CompressGZIP(file_geometry_proto_rawDescData)
})
return file_geometry_proto_rawDescData
}
var file_geometry_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_geometry_proto_goTypes = []interface{}{
(*Direction)(nil), // 0: sharedprotos.Direction
(*Vec2D)(nil), // 1: sharedprotos.Vec2D
(*Polygon2D)(nil), // 2: sharedprotos.Polygon2D
(*Vec2DList)(nil), // 3: sharedprotos.Vec2DList
(*Polygon2DList)(nil), // 4: sharedprotos.Polygon2DList
}
var file_geometry_proto_depIdxs = []int32{
1, // 0: sharedprotos.Polygon2D.anchor:type_name -> sharedprotos.Vec2D
1, // 1: sharedprotos.Polygon2D.points:type_name -> sharedprotos.Vec2D
1, // 2: sharedprotos.Vec2DList.eles:type_name -> sharedprotos.Vec2D
2, // 3: sharedprotos.Polygon2DList.eles:type_name -> sharedprotos.Polygon2D
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_geometry_proto_init() }
func file_geometry_proto_init() {
if File_geometry_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_geometry_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Direction); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_geometry_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Vec2D); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_geometry_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Polygon2D); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_geometry_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Vec2DList); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_geometry_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Polygon2DList); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_geometry_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_geometry_proto_goTypes,
DependencyIndexes: file_geometry_proto_depIdxs,
MessageInfos: file_geometry_proto_msgTypes,
}.Build()
File_geometry_proto = out.File
file_geometry_proto_rawDesc = nil
file_geometry_proto_goTypes = nil
file_geometry_proto_depIdxs = nil
}

View File

@@ -1,17 +1,15 @@
package models
package dnmshared
import (
"bytes"
"compress/zlib"
. "dnmshared/sharedprotos"
"encoding/base64"
"encoding/xml"
"errors"
"fmt"
"github.com/ByteArena/box2d"
"go.uber.org/zap"
"io/ioutil"
"math"
. "server/common"
"strconv"
"strings"
)
@@ -19,8 +17,7 @@ import (
const (
LOW_SCORE_TREASURE_TYPE = 1
HIGH_SCORE_TREASURE_TYPE = 2
SPEED_SHOES_TYPE = 3
SPEED_SHOES_TYPE = 3
LOW_SCORE_TREASURE_SCORE = 100
HIGH_SCORE_TREASURE_SCORE = 200
@@ -177,22 +174,15 @@ func (l *TmxLayer) decodeBase64() ([]uint32, error) {
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".
type StrToVec2DListMap map[string]*Vec2DList
type StrToPolygon2DListMap map[string]*Polygon2DList
func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
func tmxPolylineToPolygon2D(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
if nil == targetPolyline {
return nil, nil
}
singleValueArray := strings.Split(targetPolyline.Points, " ")
pointsCount := len(singleValueArray)
if pointsCount >= box2d.B2_maxPolygonVertices {
return nil, errors.New(fmt.Sprintf("During `TmxPolylineToPolygon2DInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v, of polyines [%v]", pointsCount, box2d.B2_maxPolygonVertices, singleValueArray))
}
theUntransformedAnchor := &Vec2D{
X: singleObjInTmxFile.X,
@@ -218,7 +208,6 @@ func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *Tmx
}
}
// Transform to B2World space coordinate.
tmp := &Vec2D{
X: thePolygon2DFromPolyline.Points[k].X,
Y: thePolygon2DFromPolyline.Points[k].Y,
@@ -231,7 +220,7 @@ func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *Tmx
return thePolygon2DFromPolyline, nil
}
func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) {
func tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) {
if nil == targetPolyline {
return nil, nil
}
@@ -242,19 +231,13 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT
singleValueArray := strings.Split(targetPolyline.Points, " ")
pointsCount := len(singleValueArray)
if pointsCount >= box2d.B2_maxPolygonVertices {
return nil, errors.New(fmt.Sprintf("During `TsxPolylineToOffsetsWrtTileCenterInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v", pointsCount, box2d.B2_maxPolygonVertices))
}
thePolygon2DFromPolyline := &Polygon2D{
Anchor: nil,
Points: make([]*Vec2D, pointsCount),
TileWidth: pTsxIns.TileWidth,
TileHeight: pTsxIns.TileHeight,
Anchor: nil,
Points: make([]*Vec2D, pointsCount),
}
/*
[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 we calculate that "thePolygon2DFromPolyline.Points" are "offsets(in B2World) w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details.
[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 {
@@ -272,14 +255,12 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT
thePolygon2DFromPolyline.Points[k].Y = float64(pTsxIns.TileHeight) - (coordinateValue + offsetFromTopLeftInTileLocalCoordY)
}
}
// No need to transform for B2World space coordinate because the marks in a Tsx file is already rectilinear.
}
return thePolygon2DFromPolyline, nil
}
func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) error {
func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMap map[int]StrToPolygon2DListMap) error {
pTsxIns := &Tsx{}
err := xml.Unmarshal(byteArrOfTsxFile, pTsxIns)
if nil != err {
@@ -297,7 +278,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
for _, tile := range pTsxIns.Tiles {
globalGid := (firstGid + int(tile.Id))
/**
Per tile xml str could be
A tile xml string could be
```
<tile id="13">
@@ -313,7 +294,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
```
, 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" and then into the corresponding "B2BodyDef & B2Body in the `world of colliding bodies`".
Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D".
*/
theObjGroup := tile.ObjectGroup
@@ -332,44 +313,36 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
key := singleObj.Properties.Property[0].Value
var theStrToPolygon2DListMap StrToPolygon2DListMap
if existingStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[globalGid]; ok {
if existingStrToPolygon2DListMap, ok := gidBoundariesMap[globalGid]; ok {
theStrToPolygon2DListMap = existingStrToPolygon2DListMap
} else {
gidBoundariesMapInB2World[globalGid] = make(StrToPolygon2DListMap, 0)
theStrToPolygon2DListMap = gidBoundariesMapInB2World[globalGid]
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]
pThePolygon2DList = &Polygon2DList{
Eles: make([]*Polygon2D, 0),
}
theStrToPolygon2DListMap[key] = pThePolygon2DList
}
thePolygon2DFromPolyline, err := TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
if nil != err {
panic(err)
}
*pThePolygon2DList = append(*pThePolygon2DList, thePolygon2DFromPolyline)
pThePolygon2DList.Eles = append(pThePolygon2DList.Eles, thePolygon2DFromPolyline)
}
}
return nil
}
func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) {
func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) {
toRetStrToVec2DListMap := make(StrToVec2DListMap, 0)
toRetStrToPolygon2DListMap := make(StrToPolygon2DListMap, 0)
/*
Note that both
- "Vec2D"s of "toRetStrToVec2DListMap", and
- "Polygon2D"s of "toRetStrToPolygon2DListMap"
are already transformed into the "coordinate of B2World".
-- YFLu
*/
for _, objGroup := range pTmxMapIns.ObjectGroups {
switch objGroup.Name {
@@ -377,35 +350,29 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i
var pTheVec2DListToCache *Vec2DList
_, ok := toRetStrToVec2DListMap[objGroup.Name]
if false == ok {
theVec2DListToCache := make(Vec2DList, 0)
toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
} else {
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
pTheVec2DListToCache = &Vec2DList{
Eles: make([]*Vec2D, 0),
}
toRetStrToVec2DListMap[objGroup.Name] = pTheVec2DListToCache
}
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)
pTheVec2DListToCache.Eles = append(pTheVec2DListToCache.Eles, &thePosInWorld)
}
case "Pumpkin", "SpeedShoe":
case "Barrier":
/*
Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is located exactly in an overlapping with "Polygon2D.Points[0]" w.r.t. B2World.
-- YFLu
*/
// 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]
} else {
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
pThePolygon2DListToCache = &Polygon2DList{
Eles: make([]*Polygon2D, 0),
}
toRetStrToPolygon2DListMap[objGroup.Name] = pThePolygon2DListToCache
}
for _, singleObjInTmxFile := range objGroup.Objects {
@@ -416,70 +383,11 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i
continue
}
thePolygon2DInWorld, err := TmxPolylineToPolygon2DInB2World(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline)
thePolygon2DInWorld, err := tmxPolylineToPolygon2D(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline)
if nil != err {
panic(err)
}
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
}
case "LowScoreTreasure", "GuardTower", "HighScoreTreasure":
/*
Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" ISN'T located exactly in an overlapping with "Polygon2D.Points[0]" w.r.t. B2World, refer to "https://shimo.im/docs/SmLJJhXm2C8XMzZT" for details.
-- YFLu
*/
for _, singleObjInTmxFile := range objGroup.Objects {
if nil == singleObjInTmxFile.Gid {
continue
}
theGlobalGid := singleObjInTmxFile.Gid
theStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[*theGlobalGid]
if false == ok {
continue
}
pThePolygon2DList, ok := theStrToPolygon2DListMap[objGroup.Name]
if false == ok {
continue
}
var pThePolygon2DListToCache *Polygon2DList
_, ok = toRetStrToPolygon2DListMap[objGroup.Name]
if false == ok {
thePolygon2DListToCache := make(Polygon2DList, 0)
toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
} else {
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
}
for _, thePolygon2D := range *pThePolygon2DList {
theUntransformedBottomCenterAsAnchor := &Vec2D{
X: singleObjInTmxFile.X,
Y: singleObjInTmxFile.Y,
}
theTransformedBottomCenterAsAnchor := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedBottomCenterAsAnchor)
thePolygon2DInWorld := &Polygon2D{
Anchor: &theTransformedBottomCenterAsAnchor,
Points: make([]*Vec2D, len(thePolygon2D.Points)),
TileWidth: thePolygon2D.TileWidth,
TileHeight: thePolygon2D.TileHeight,
}
if nil != singleObjInTmxFile.Width && nil != singleObjInTmxFile.Height {
thePolygon2DInWorld.TmxObjectWidth = *singleObjInTmxFile.Width
thePolygon2DInWorld.TmxObjectHeight = *singleObjInTmxFile.Height
}
for kk, p := range thePolygon2D.Points {
// [WARNING] It's intentionally recreating a copy of "Vec2D" here.
thePolygon2DInWorld.Points[kk] = &Vec2D{
X: p.X,
Y: p.Y,
}
}
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
}
pThePolygon2DListToCache.Eles = append(pThePolygon2DListToCache.Eles, thePolygon2DInWorld)
}
default:
}
@@ -498,6 +406,12 @@ type TileRectilinearSize struct {
}
func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerVec *Vec2D) Vec2D {
if "orthogonal" == pTmxMapIns.Orientation {
return Vec2D{
X: continuousObjLayerVec.X,
Y: -continuousObjLayerVec.Y,
}
}
var tileRectilinearSize TileRectilinearSize
tileRectilinearSize.Width = float64(pTmxMapIns.TileWidth)
tileRectilinearSize.Height = float64(pTmxMapIns.TileHeight)
@@ -520,18 +434,24 @@ func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuous
}
func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continuousObjLayerOffset *Vec2D) Vec2D {
layerOffset := Vec2D{
X: 0,
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
var layerOffset Vec2D
if "orthogonal" == pTmxMapIns.Orientation {
layerOffset = Vec2D{
X: -float64(pTmxMapIns.Width*pTmxMapIns.TileWidth) * 0.5,
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
}
} else {
// "isometric" == pTmxMapIns.Orientation
layerOffset = Vec2D{
X: 0,
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
}
}
calibratedVec := continuousObjLayerOffset
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(calibratedVec)
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerOffset)
toRet := Vec2D{
return Vec2D{
X: layerOffset.X + convertedVec.X,
Y: layerOffset.Y + convertedVec.Y,
}
return toRet
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
{"width":128,"imagePath":"SoldierElf_tex.png","height":128,"name":"SoldierElf","SubTexture":[{"frameHeight":45,"y":1,"frameX":0,"width":34,"frameY":0,"height":44,"name":"cape","frameWidth":34,"x":70},{"width":10,"y":107,"height":14,"name":"shouder_l","x":74},{"width":11,"y":107,"height":14,"name":"forearm_l","x":61},{"width":15,"y":93,"height":16,"name":"hand_l","x":1},{"width":30,"y":61,"height":30,"name":"weapon_hand_l","x":1},{"width":8,"y":101,"height":11,"name":"thigh_l","x":86},{"width":12,"y":93,"height":17,"name":"calf_l","x":18},{"width":20,"y":113,"height":8,"name":"foot_l","x":39},{"width":28,"y":61,"height":31,"name":"pelvis","x":33},{"width":8,"y":88,"height":11,"name":"thigh_r","x":77},{"width":12,"y":88,"height":17,"name":"calf_r","x":63},{"width":20,"y":113,"height":8,"name":"foot_r","x":17},{"width":13,"y":94,"height":12,"name":"shouder_r","x":45},{"width":67,"y":1,"height":58,"name":"chest","x":1},{"width":11,"y":94,"height":17,"name":"forearm_r","x":32},{"width":14,"y":111,"height":13,"name":"hand_r","x":1},{"frameHeight":39,"y":47,"frameX":-2,"width":34,"frameY":0,"height":39,"name":"we_bl_4_f_1","frameWidth":36,"x":70}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1 @@
{"imagePath":"Soldier_02_tex.png","width":128,"name":"Soldier_02","SubTexture":[{"x":53,"y":44,"width":23,"name":"biu","height":22},{"x":76,"y":68,"width":9,"name":"rightArm","height":14},{"y":35,"frameY":0,"height":32,"frameWidth":29,"frameX":-1,"frameHeight":32,"width":27,"name":"yinmoqe00","x":89},{"x":53,"y":1,"width":34,"name":"body","height":41},{"x":78,"y":44,"width":9,"name":"rightShoulder","height":13},{"y":50,"frameY":0,"height":18,"frameWidth":19,"frameX":0,"frameHeight":18,"width":19,"name":"rightFrontArm","x":23},{"x":23,"y":70,"width":14,"name":"rightHand","height":14},{"y":68,"frameY":0,"height":12,"frameWidth":12,"frameX":0,"frameHeight":12,"width":12,"name":"leftArm","x":62},{"x":1,"y":73,"width":13,"name":"leftShoulder","height":12},{"x":1,"y":50,"width":20,"name":"leftFrontArm","height":21},{"x":89,"y":1,"width":33,"name":"head","height":32},{"x":1,"y":1,"width":50,"name":"head2","height":47},{"x":44,"y":68,"width":16,"name":"leftHand","height":14},{"y":59,"frameY":-2,"height":4,"frameWidth":8,"frameX":-1,"frameHeight":8,"width":4,"name":"huomiao01","x":78}],"height":128}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -1,6 +1,5 @@
"use strict";
var _ROUTE_PATH;
function _defineProperty(obj, key, value) {
@@ -29,23 +28,6 @@ var constants = {
BGM: "BGM"
}
},
PLAYER_NAME: {
1: "Merdan",
2: "Monroe",
},
SOCKET_EVENT: {
CONTROL: "control",
SYNC: "sync",
LOGIN: "login",
CREATE: "create"
},
WECHAT: {
AUTHORIZE_PATH: "/connect/oauth2/authorize",
REDIRECT_RUI_KEY: "redirect_uri=",
RESPONSE_TYPE: "response_type=code",
SCOPE: "scope=snsapi_userinfo",
FIN: "#wechat_redirect"
},
ROUTE_PATH: (_ROUTE_PATH = {
PLAYER: "/player",
JSCONFIG: "/jsconfig",
@@ -61,8 +43,6 @@ var constants = {
LIST: "/list",
READ: "/read",
PROFILE: "/profile",
WECHAT: "/wechat",
WECHATGAME: "/wechatGame",
FETCH: "/fetch",
}, _defineProperty(_ROUTE_PATH, "LOGIN", "/login"), _defineProperty(_ROUTE_PATH, "RET_CODE", "/retCode"), _defineProperty(_ROUTE_PATH, "REGEX", "/regex"), _defineProperty(_ROUTE_PATH, "SMS_CAPTCHA", "/SmsCaptcha"), _defineProperty(_ROUTE_PATH, "GET", "/get"), _ROUTE_PATH),
REQUEST_QUERY: {
@@ -138,7 +118,6 @@ var constants = {
INCORRECT_PHONE_NUMBER: '手机号不正确',
LOG_OUT: '您已在其他地方登陆',
GAME_OVER: '游戏结束,您的得分是',
WECHAT_LOGIN_FAILS: "微信登录失败",
},
CONFIRM_BUTTON_LABEL: {
RESTART: '重新开始'

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
{
"ver": "1.0.5",
"uuid": "bd514df4-095e-4088-9060-d99397a29a4f",
"isPlugin": true,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -1,6 +1,6 @@
{
"ver": "1.0.1",
"uuid": "33d33d2f-5ca7-4677-9c82-4ced71aa0f8a",
"uuid": "2202f4f4-b792-4dea-8302-633315aded66",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"width":128,"SubTexture":[{"frameWidth":34,"y":1,"frameHeight":45,"width":34,"frameX":0,"height":44,"name":"cape","frameY":0,"x":70},{"width":10,"y":107,"height":14,"name":"shouder_l","x":74},{"width":11,"y":107,"height":14,"name":"forearm_l","x":61},{"width":15,"y":93,"height":16,"name":"hand_l","x":1},{"width":30,"y":61,"height":30,"name":"weapon_hand_l","x":1},{"width":8,"y":88,"height":11,"name":"thigh_l","x":77},{"width":12,"y":93,"height":17,"name":"calf_l","x":18},{"width":20,"y":113,"height":8,"name":"foot_l","x":39},{"width":28,"y":61,"height":31,"name":"pelvis","x":33},{"width":8,"y":101,"height":11,"name":"thigh_r","x":86},{"width":12,"y":88,"height":17,"name":"calf_r","x":63},{"width":20,"y":113,"height":8,"name":"foot_r","x":17},{"width":13,"y":94,"height":12,"name":"shouder_r","x":45},{"width":67,"y":1,"height":58,"name":"chest","x":1},{"width":11,"y":94,"height":17,"name":"forearm_r","x":32},{"width":14,"y":111,"height":13,"name":"hand_r","x":1},{"frameWidth":36,"y":47,"frameHeight":39,"width":34,"frameX":-2,"height":39,"name":"we_bl_4_f_1","frameY":0,"x":70}],"height":128,"name":"SoldierElf","imagePath":"SoldierElf_tex.png"}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.0",
"uuid": "24d7bb8f-577c-4e5d-b730-56613ca8685d",
"atlasJson": "{\"width\":128,\"SubTexture\":[{\"frameWidth\":34,\"y\":1,\"frameHeight\":45,\"width\":34,\"frameX\":0,\"height\":44,\"name\":\"cape\",\"frameY\":0,\"x\":70},{\"width\":10,\"y\":107,\"height\":14,\"name\":\"shouder_l\",\"x\":74},{\"width\":11,\"y\":107,\"height\":14,\"name\":\"forearm_l\",\"x\":61},{\"width\":15,\"y\":93,\"height\":16,\"name\":\"hand_l\",\"x\":1},{\"width\":30,\"y\":61,\"height\":30,\"name\":\"weapon_hand_l\",\"x\":1},{\"width\":8,\"y\":88,\"height\":11,\"name\":\"thigh_l\",\"x\":77},{\"width\":12,\"y\":93,\"height\":17,\"name\":\"calf_l\",\"x\":18},{\"width\":20,\"y\":113,\"height\":8,\"name\":\"foot_l\",\"x\":39},{\"width\":28,\"y\":61,\"height\":31,\"name\":\"pelvis\",\"x\":33},{\"width\":8,\"y\":101,\"height\":11,\"name\":\"thigh_r\",\"x\":86},{\"width\":12,\"y\":88,\"height\":17,\"name\":\"calf_r\",\"x\":63},{\"width\":20,\"y\":113,\"height\":8,\"name\":\"foot_r\",\"x\":17},{\"width\":13,\"y\":94,\"height\":12,\"name\":\"shouder_r\",\"x\":45},{\"width\":67,\"y\":1,\"height\":58,\"name\":\"chest\",\"x\":1},{\"width\":11,\"y\":94,\"height\":17,\"name\":\"forearm_r\",\"x\":32},{\"width\":14,\"y\":111,\"height\":13,\"name\":\"hand_r\",\"x\":1},{\"frameWidth\":36,\"y\":47,\"frameHeight\":39,\"width\":34,\"frameX\":-2,\"height\":39,\"name\":\"we_bl_4_f_1\",\"frameY\":0,\"x\":70}],\"height\":128,\"name\":\"SoldierElf\",\"imagePath\":\"SoldierElf_tex.png\"}",
"texture": "050fb016-1a1f-4341-8367-283bfeddc4a8",
"subMetas": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,6 +1,6 @@
{
"ver": "2.3.3",
"uuid": "0a5e9b39-6a98-4b45-9ad5-6df3effec2ef",
"uuid": "050fb016-1a1f-4341-8367-283bfeddc4a8",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
@@ -9,21 +9,21 @@
"packable": true,
"platformSettings": {},
"subMetas": {
"Tile_W300_H300_S01": {
"SoldierElf_tex": {
"ver": "1.0.4",
"uuid": "acb30663-c0cf-4727-a046-26e40610c49f",
"rawTextureUuid": "0a5e9b39-6a98-4b45-9ad5-6df3effec2ef",
"uuid": "c62e1779-f92b-40d3-bf4f-7ab747e33d6e",
"rawTextureUuid": "050fb016-1a1f-4341-8367-283bfeddc4a8",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 4,
"offsetY": -24.5,
"trimX": 97,
"trimY": 85,
"width": 114,
"height": 179,
"rawWidth": 300,
"rawHeight": 300,
"offsetX": -11.5,
"offsetY": 1.5,
"trimX": 1,
"trimY": 1,
"width": 103,
"height": 123,
"rawWidth": 128,
"rawHeight": 128,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,

View File

@@ -1,6 +1,6 @@
{
"ver": "1.0.1",
"uuid": "f9460fe9-26ad-4e4b-8a3c-c97bef705a71",
"uuid": "f1176719-d1d6-4af5-89c6-ddff16ab85fd",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"width":128,"SubTexture":[{"frameWidth":23,"y":44,"frameHeight":22,"width":22,"frameX":-1,"height":22,"name":"biu","frameY":0,"x":53},{"width":9,"y":68,"height":14,"name":"rightArm","x":76},{"frameWidth":29,"y":35,"frameHeight":32,"width":26,"frameX":-1,"height":32,"name":"yinmoqe00","frameY":0,"x":89},{"width":34,"y":1,"height":41,"name":"body","x":53},{"width":9,"y":44,"height":13,"name":"rightShoulder","x":77},{"width":19,"y":50,"height":18,"name":"rightFrontArm","x":23},{"width":14,"y":70,"height":14,"name":"rightHand","x":23},{"width":12,"y":68,"height":12,"name":"leftArm","x":62},{"width":13,"y":73,"height":12,"name":"leftShoulder","x":1},{"width":20,"y":50,"height":21,"name":"leftFrontArm","x":1},{"width":33,"y":1,"height":32,"name":"head","x":89},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"width":16,"y":68,"height":14,"name":"leftHand","x":44},{"frameWidth":8,"y":59,"frameHeight":8,"width":4,"frameX":-1,"height":4,"name":"huomiao01","frameY":-2,"x":77}],"height":128,"name":"SoldierFireGhost","imagePath":"SoldierFireGhost_tex.png"}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.0",
"uuid": "4a9187d5-a9ad-4464-a03c-d2f3cc277051",
"atlasJson": "{\"width\":128,\"SubTexture\":[{\"frameWidth\":23,\"y\":44,\"frameHeight\":22,\"width\":22,\"frameX\":-1,\"height\":22,\"name\":\"biu\",\"frameY\":0,\"x\":53},{\"width\":9,\"y\":68,\"height\":14,\"name\":\"rightArm\",\"x\":76},{\"frameWidth\":29,\"y\":35,\"frameHeight\":32,\"width\":26,\"frameX\":-1,\"height\":32,\"name\":\"yinmoqe00\",\"frameY\":0,\"x\":89},{\"width\":34,\"y\":1,\"height\":41,\"name\":\"body\",\"x\":53},{\"width\":9,\"y\":44,\"height\":13,\"name\":\"rightShoulder\",\"x\":77},{\"width\":19,\"y\":50,\"height\":18,\"name\":\"rightFrontArm\",\"x\":23},{\"width\":14,\"y\":70,\"height\":14,\"name\":\"rightHand\",\"x\":23},{\"width\":12,\"y\":68,\"height\":12,\"name\":\"leftArm\",\"x\":62},{\"width\":13,\"y\":73,\"height\":12,\"name\":\"leftShoulder\",\"x\":1},{\"width\":20,\"y\":50,\"height\":21,\"name\":\"leftFrontArm\",\"x\":1},{\"width\":33,\"y\":1,\"height\":32,\"name\":\"head\",\"x\":89},{\"width\":50,\"y\":1,\"height\":47,\"name\":\"head2\",\"x\":1},{\"width\":16,\"y\":68,\"height\":14,\"name\":\"leftHand\",\"x\":44},{\"frameWidth\":8,\"y\":59,\"frameHeight\":8,\"width\":4,\"frameX\":-1,\"height\":4,\"name\":\"huomiao01\",\"frameY\":-2,\"x\":77}],\"height\":128,\"name\":\"SoldierFireGhost\",\"imagePath\":\"SoldierFireGhost_tex.png\"}",
"texture": "700d963b-2192-4219-a066-8be5b3db7453",
"subMetas": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -0,0 +1,34 @@
{
"ver": "2.3.3",
"uuid": "700d963b-2192-4219-a066-8be5b3db7453",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
"premultiplyAlpha": false,
"genMipmaps": false,
"packable": true,
"platformSettings": {},
"subMetas": {
"SoldierFireGhost_tex": {
"ver": "1.0.4",
"uuid": "8ef8a6b3-0bac-4cf1-bba0-ab090f4d9e52",
"rawTextureUuid": "700d963b-2192-4219-a066-8be5b3db7453",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": -2.5,
"offsetY": 21,
"trimX": 1,
"trimY": 1,
"width": 121,
"height": 84,
"rawWidth": 128,
"rawHeight": 128,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"subMetas": {}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "5650b341-a420-4d79-a969-39a461e13378",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="64" height="64" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="12">
<tileset firstgid="1" source="tiles0.tsx"/>
<tileset firstgid="65" source="tiles1.tsx"/>
<layer id="1" name="Ground" width="64" height="64">
<data encoding="base64" compression="zlib">
eJzt0jEOwjAUREGLkiLmALRU3In7HwMKkFCUCCwiNrKnmCbVvh8fSikHAAAA/qruYEOr84Zd+vM9re0v+vXr1z9S/7f0l3KZ9dXZ91r67QeAXxyf0jv0Z/tvO9iS7F+T3ucG+Xuk96RvkN4CMJLaIL0VttLy7r19euHdA+zX6YP0vlR3z/3ax2tfarw+TAO0L/VPb3ruXvv3o7z7lv70zuQN0vtgz+6DDSsZ
</data>
</layer>
<objectgroup id="1" name="PlayerStartingPos">
<object id="135" x="512" y="512">
<point/>
</object>
<object id="137" x="640" y="640">
<point/>
</object>
</objectgroup>
<objectgroup id="2" name="Barrier">
<properties>
<property name="type" value="barrier_and_shelter"/>
</properties>
<object id="1" x="400" y="224.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,141.5 17,141.5 17,-1"/>
</object>
<object id="2" x="559.5" y="207.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 1,158.5 17,158.5 17,0"/>
</object>
<object id="3" x="448.5" y="353">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,14.5 -49,14.5 -49,-1.5"/>
</object>
<object id="4" x="577.5" y="351.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -98,1 -98,16 -1,15.5"/>
</object>
<object id="5" x="449.333" y="654.667">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -178.667,0.666667 -178.667,17.3333 -0.666667,17.3333"/>
</object>
<object id="6" x="432.5" y="703.5">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -191.667,0.666667 -191.667,17.3333 -0.715174,17.3333"/>
</object>
<object id="7" x="400" y="751">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 -191.667,0.666667 -191.667,17.3333 -0.715174,17.3333"/>
</object>
<object id="8" x="-9.33333" y="-13.3333">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
</object>
<object id="9" x="-9.33333" y="1014.67">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
</object>
<object id="10" x="-14" y="-40">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 4,1110 24,1110 24,-8"/>
</object>
<object id="11" x="1014" y="-42">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
<polyline points="0,0 4,1110 24,1110 24,-8"/>
</object>
</objectgroup>
</map>

View File

@@ -0,0 +1,5 @@
{
"ver": "2.0.2",
"uuid": "1b802c87-1978-4c6a-bd0b-1f6b8526b3ad",
"subMetas": {}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.3" name="tiles0" tilewidth="16" tileheight="16" tilecount="64" columns="16">
<image source="watabou_pixel_dungeon_orig_files/tiles0.png" width="256" height="64"/>
</tileset>

View File

@@ -0,0 +1,5 @@
{
"ver": "2.0.0",
"uuid": "d4cf0e72-7454-4310-b975-beb5e81a63ae",
"subMetas": {}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.3" name="tiles1" tilewidth="16" tileheight="16" tilecount="64" columns="16">
<image source="watabou_pixel_dungeon_orig_files/tiles1.png" width="256" height="64"/>
</tileset>

View File

@@ -0,0 +1,5 @@
{
"ver": "2.0.0",
"uuid": "0bcabaac-a406-4b3d-9285-814e00c5b09d",
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "2a231040-0e69-42b4-a35b-9690e976e71a",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,5 @@
{
"ver": "2.0.0",
"uuid": "acb40b5d-372b-4502-a6bf-f756908f8221",
"subMetas": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,6 +1,6 @@
{
"ver": "2.3.3",
"uuid": "757f31be-7ad1-42dc-bd6b-2b4c7652e457",
"uuid": "94f42afa-43e1-4936-b5ac-7bcfe252f9e1",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
@@ -9,21 +9,21 @@
"packable": true,
"platformSettings": {},
"subMetas": {
"Tile_W256_H256_S01": {
"amulet": {
"ver": "1.0.4",
"uuid": "0d9aafd3-d738-473c-95b8-bf577b78f03d",
"rawTextureUuid": "757f31be-7ad1-42dc-bd6b-2b4c7652e457",
"uuid": "52580025-ae7a-4c48-878f-f685bc90e737",
"rawTextureUuid": "94f42afa-43e1-4936-b5ac-7bcfe252f9e1",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 64,
"width": 1280,
"height": 896,
"rawWidth": 1280,
"rawHeight": 1024,
"trimY": 0,
"width": 32,
"height": 32,
"rawWidth": 32,
"rawHeight": 32,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

View File

@@ -1,6 +1,6 @@
{
"ver": "2.3.3",
"uuid": "b711d3f8-b9f4-41ab-a7e4-6562992fb65e",
"uuid": "a9781031-1dba-4214-9167-080fc1920f92",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
@@ -9,21 +9,21 @@
"packable": true,
"platformSettings": {},
"subMetas": {
"Tile_W256_H128_S01": {
"arcs1": {
"ver": "1.0.4",
"uuid": "2d6c9481-6bd5-4c19-b468-529d027226c3",
"rawTextureUuid": "b711d3f8-b9f4-41ab-a7e4-6562992fb65e",
"uuid": "5430edd4-3e0b-4509-a575-d7c7f8db4e31",
"rawTextureUuid": "a9781031-1dba-4214-9167-080fc1920f92",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 831,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 2048,
"height": 386,
"rawWidth": 2048,
"rawHeight": 2048,
"width": 32,
"height": 32,
"rawWidth": 32,
"rawHeight": 32,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

View File

@@ -1,6 +1,6 @@
{
"ver": "2.3.3",
"uuid": "2385ca17-af24-4122-a271-785f3010d7f2",
"uuid": "940f2fcc-df03-46ed-ad32-d92880efe501",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
@@ -9,21 +9,21 @@
"packable": true,
"platformSettings": {},
"subMetas": {
"Tile_W256_H256_S02": {
"arcs2": {
"ver": "1.0.4",
"uuid": "34e3f356-e65d-4745-8f70-7706aa4083f8",
"rawTextureUuid": "2385ca17-af24-4122-a271-785f3010d7f2",
"uuid": "11915948-c7bf-41db-b6eb-74b364df7688",
"rawTextureUuid": "940f2fcc-df03-46ed-ad32-d92880efe501",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 19.5,
"offsetY": 3.5,
"trimX": 89,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 1409,
"height": 251,
"rawWidth": 1548,
"rawHeight": 258,
"width": 64,
"height": 64,
"rawWidth": 64,
"rawHeight": 64,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,34 @@
{
"ver": "2.3.3",
"uuid": "ac193c85-fb5d-4d66-b911-81e2254e0009",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
"premultiplyAlpha": false,
"genMipmaps": false,
"packable": true,
"platformSettings": {},
"subMetas": {
"avatars": {
"ver": "1.0.4",
"uuid": "b472e0d1-c866-4a4d-b8f9-4daca275b8db",
"rawTextureUuid": "ac193c85-fb5d-4d66-b911-81e2254e0009",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": -16.5,
"offsetY": 2,
"trimX": 2,
"trimY": 0,
"width": 91,
"height": 28,
"rawWidth": 128,
"rawHeight": 32,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"subMetas": {}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1,34 @@
{
"ver": "2.3.3",
"uuid": "f204f568-cf6e-462e-834e-ef6961dae26e",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
"premultiplyAlpha": false,
"genMipmaps": false,
"packable": true,
"platformSettings": {},
"subMetas": {
"badges": {
"ver": "1.0.4",
"uuid": "fec2a102-8c77-4528-8130-488184e8bf38",
"rawTextureUuid": "f204f568-cf6e-462e-834e-ef6961dae26e",
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 128,
"height": 128,
"rawWidth": 128,
"rawHeight": 128,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"subMetas": {}
}
}
}

Some files were not shown because too many files have changed in this diff Show More