0. Preface

This repository introduces an intact "building & crafting & tower-defense/siege" game with "remotely persisted profile" as well as "pull-based progress update management".

It supports a wide variety of features as shown in the intro videos listed below, yet not currently integrated into a single entry scene. If you find some features "too hidden to enable", please leave a message in Github issues.

It's strongly recommended that you start playing with this repository with the "frontend-only tower-siege game", i.e. at <proj-root>/frontend/assets/scenes/OfflineStageMap.fire.

We decided to open source this project due to lack of resource to maintain it, therefore new feature proposal, bug report or pull-request might not be handled in time -- and we'll try our best.

1. Database Server

The database product to be used for this project is MySQL 5.7.

We use skeema for schematic synchronization under <proj-root>/database/skeema-repo-root/ which intentionally doesn't contain a .skeema file. Please read this tutorial for more information.

You can use this node module (still under development) instead under Windows10, other versions of Windows are not yet tested for compatibility.

The following command(s)

### Optional.
user@proj-root/database/skeema-repo-root> cp .skeema.template .skeema

###
user@proj-root/database/skeema-repo-root> skeema diff

is recommended to be used for checking difference from your "live MySQL server" to the latest expected schema tracked in git.

1.1 Database-related-items checklist before pushing to a collaborative branch

Before pushing to, say the "master" branch, you should be very careful about the preconfigured tables in SQLite. Mostly they're tables to be dumped into "MySQLServer tables with the same names" respectively, e.g. "SQLite table buildable" will be dumped into "runtime MySQLServer table buildable" upon server process(es) launch (using <proj-root>/battle_srv/env_tools/load_pre_conf.go), and in turn loaded to instances of in-RAM model class Buildable defined in <proj-root>/battle_srv/models/buildable.

In a "clean commit", you should make sure that each table involved in ToSyncSQLFiles

  • <proj-root>/battle_srv/configs.template/*.sqlite
  • <proj-root>/database/skeema-repo-root/*.sql
  • <proj-root>/battle_srv/models/*.go

is made "git-pullable", such that a colleague who pulls your commit is able to launch the server process(es) if he/she at least overwrites the runtime environment with all the ToSyncSQLFiles of the current commit.

2. Building & running

2.1 Golang1.11

See https://github.com/genxium/Go111ModulePrac for details.

2.2 MySQL

On a product machine, you can install and manage MySQL server by these scripts.

2.3 Required Config Files

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.

2.3.2 Frontend

  • It needs CocosCreator v2.2.1 to build.
  • 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.

2.4 Different "ServerEnv"s for the runtime of backend service

本游戏具备账号系统且主要注册方式为"Phonenum+SMSCaptcha",考虑到开发者和公司内部测试的需要,服务器将提供以下的"ServerEnv"s供在runtime of backend service按需采用。

2.4.1 ServerEnv == PROD (如不指定ServerEnv则默认为PROD)

user@proj-root/battle_srv> make run-prod

将不允许使用“测试账号”。

2.4.2 ServerEnv == TEST

user@proj-root/battle_srv> make run-test

将允许使用“测试账号”,详见 https://shimo.im/docs/Q7wqNHlvyYQu0nYP 说明。

以下两个ServerEnv可以使用"iPhone游客登录"方式试玩,此开关在battle_srv/configs/global_conf.jsonanonymousPlayerEnabled中配置。

2.4.3 ServerEnv == AnonymousPlayerEnabledTest

user@proj-root/battle_srv> make run-anonymous-test

2.4.4 ServerEnv == AnonymousPlayerEnabledProd

user@proj-root/battle_srv> make run-anonymous-prod

你也可以同时加用hotreload。

## 安装CompileDaemon,路径默认是$GOPATH/bin/CompileDaemon
user@proj-root/battle_srv> go get github.com/githubnemo/CompileDaemon 
user@proj-root/battle_srv> make run-test-and-hotreload

For clearing certain hardcoded test accounts (in <proj-root>/battle_srv/cli_scripts/player_test.go), try the following one-liner.

For updatePlayerCookBinding started_work_at test (in <proj-root>/battle_srv/cli_scripts/player_test.go),now this script will update all playerCookBinding started_work_at,if you want update one playerCookBinding started_work_at,you can add WHERE("player_id": playerId) at <proj-root>/battle_srv/cli_scripts/player_test.go test UpdatePlayerCookBindingStartedWorkAt

For updatePlayerWallet gold,energy,daemon test (in <proj-root>/battle_srv/cli_scripts/player_test.go),this script will update playerId in scripts's playerIds ,if you want update your test player player_wallte,you can add update at <proj-root>/battle_srv/cli_scripts/player_test.go UpdatePlayerWallet() playerIds

user@proj-root/battle_srv> cp -r ./cli_scripts.template ./cli_scripts

## Then customize the parameters in ./cli_scripts/*.go for your convenience, e.g. the TestEnv player IDs.
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run DeletePlayer
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run InitKnapsack
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run InitKnapsackForExistedUser
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run IngredientProduce --args -cancel=true
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run IngredientProduce --args -cancel=false
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run SynthesizeToIngredientProgress --args -cancel=true
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run SynthesizeToIngredientProgress --args -cancel=false
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run SynthesizeToManuallyCollectableIngredientProgress --args -collect=true
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run SynthesizeToManuallyCollectableIngredientProgress --args -collect=false
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run MassiveCrafting
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run HqConcurrentQueues
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run SmsCaptchaLogin
user@proj-root/battle_srv> ServerEnv=TEST go test --count=1 -v server/cli_scripts -run WsConnectAttackFirstVictim

2.5 Troubleshooting

2.5.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."}

You could consider setting

redis-cli> CONFIG SET stop-writes-on-bgsave-error NO

and restarting your redis-server process if having persistnet redis snapshot is not necessary.

3. More about development

3.1 db2struct

将数据库结构转为golang的struct,注意Null的数据类型不用用生成的sql.NullInt64,要自己封装的NullInt64,支持序列化到json,否则不支持。

user@shell> go get github.com/roowe/db2struct
user@shell> db2struct -d cuisine --package models --t <table_name> --user <db_name> --password <db_passwd> --struct <struct_name> --sqlx --json

具体看:https://github.com/Shelnutt2/db2struct。

3.2 json2strust

将json file转为golang的struct。

user@shell> go get https://github.com/ChimeraCoder/gojson
user@proj-root/battle_srv/common/> gojson -name constants -pkg common -input ./constants.json -o ./constants_struct.go 

4. Building to iOS project

Due to possibly a bug in CocosCreator's custom building pipeline, after successfully building by the cmd above, you might have to manually add "/build/jsb-link/frameworks/runtime-src/Classes/*" into the "XCode project virtual directory named Classes". Refer to this note for screenshot demo.

4.1 Removing the use of IDFA in libcocosanalytics.a

Please read this note for details. I've removed the import of CAAgent from <proj-root>/frontend/build-templates/jsb-link/frameworks/runtime-src/proj.ios_mac/ios/AppController.mm, but you should follow the note to removed the dependence on libcocosanalytics.a BEFORE building the archive.

4.2 Switching between different CocosCreator versions

Please make sure that you've removed <proj-root>/frontend/build/ thoroughly before rebuilding the project by a different CocosCreator version. Otherwise JSB2.0 module registration procedures will be contaminated and you might get symbols undefined (e.g. XMLHttpRequest) upon launch.

5. Building to WechatGame

5.1 Configuring the correct appId and WechatServer endpoint

  • /battle_srv/configs/wechat.json
  • /frontend/assets/plugin_scripts/conf.js

5.2 Configuring the "REMOTE_SERVER" and uploading to corresponding remote dir

Set REMOTE_SERVER_ROOT = "https://mineralchem.lokcol.com/wechat-game/" in the build panel, and then upload by the following command(s).

shell> scp -r ./frontend/build/wechatgame/res/* ubuntu@mineralchem.lokcol.com:/var/www/html/MineralChem/wechat-game/res/
&
shell> rm -rf ./frontend/build/wechatgame/res/*

5.3 Total count of HTTP requests v.s. single large file

Either an overwhelming "count of HTTP requests" or a "single large file" will jam the download of resources at runtime. We can tune the balance in the building panel, please check/uncheck the followings.

  • Inline all SpriteFrames
    • We've already compressed the textures using TexturePacker, thus this field doesn't affect much.
  • Merge all JSON that Start Scene
    • Unchecking this field might result in too many "JSONIndexFile requests" (avoided by the approach stated below), whilst checking this field might result in a single large file.
    • By loading the "WechatGameLogin" scene first, we want to be able to level & parallelize the time spent in downloading & parsing "JSONIndexFile(s)" among different scene.
  • MD5 cache (√)

According to http://docs.cocos.com/creator/manual/en/scripting/load-assets.html#how-to-dynamically-load, "all files under the resources folder, as well as any external file they depend on", will be written into "JSONIndexFiles" after project being built (delivered only via the mainpackage). To reduce the size of such "JSONIndexFiles" for WechatGame platform, we should put as few files under resources folder as possible, e.g. for those to be called by cc.loader.loadRes(...).

6. Building and deploying to Web

shell> scp -r ./frontend/build/web-mobile/* ubuntu@idlegame.lokcol.com:/var/www/html/MineralChem/web-mobile/

7. Proactively refreshing the Redis cache of "GlobalConf" after modified the file "battle_srv/configs/global_conf"

The cold way.

# Stop the "battle_srv process" if you're going to delete the conf object in redis, otherwise the "battle_srv process" will go wrong. 
user@proj-root/battle_srv> ./stop_daemon.sh
user@proj-root> redis-cli
redis-cli@shell> DEL /cuisine/conf
redis-cli@shell> exit
user@proj-root/battle_srv> ./start_daemon.sh

The hot way.

user@proj-root> node proactively_update_global_conf_in_redis.js 

8. Upgrading from "CocosCreator v2.1.3" to "CocosCreator v2.2"

In "CocosCreator v2.2", the extension i18n is no longer needed and incompatible with the newer version, just delete <proj-root>/frontend/packages/i18n-master.

9. Building to ByteDanceMiniGame

9.1 Use CLI to build the project

- <proj-root/frontend> ./build_to_platform.sh bytedance 

9.2 Uploading to corresponding remote dir

shell@proj-root> python ./frontend/uploader_scripts/upload_to_qiniu.py -d ./frontend/build/wechatgame/res/ --prefix="wechat-game" -a <MyAppKey> -s <MySecretKey> 
shell@proj-root> rm -rf ./frontend/build/wechatgame/res/*