├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── analytics ├── anobj │ └── anobj.go ├── collector.go └── factors │ └── factors.go ├── bgtasks ├── purgeSessionIDs.go └── touchAnalyticsDB.go ├── config ├── authorizedconf │ └── authorizedParser.go ├── campaignconf │ └── campaignconfParser.go ├── eventconf │ └── eventconfParser.go ├── gameconf │ └── gameconfParser.go ├── infoconf │ └── infoconfParser.go └── parser.go ├── consts ├── campaignPointScores.go ├── chaoRouletteConsts.go ├── characterUpgradeValues.go ├── dailyMissionRewards.go ├── db.go ├── episodeChapters.go ├── gameVersionTables.go ├── itemCosts.go ├── numValues.go ├── randomChaoItemList.go ├── randomItemList.go └── rouletteConsts.go ├── cryption ├── basic.go └── cryptid.go ├── db ├── assistants.go └── dbaccess │ ├── compression.go │ └── db.go ├── emess └── error_messages.go ├── enums ├── abilityType.go ├── advertEventType.go ├── boostItemType.go ├── campaign.go ├── chaoDealing.go ├── chaoIDs.go ├── chaoRarity.go ├── chaoStatus.go ├── chaoType.go ├── chaoWheelType.go ├── charaType.go ├── characterStatus.go ├── collectEventType.go ├── dailyMissions.go ├── eventIDs.go ├── eventTypes.go ├── idTypes.go ├── ids.go ├── incentiveTypes.go ├── itemIDs.go ├── itemType.go ├── language.go ├── lockCondition.go ├── rankingLeague.go ├── rankingMode.go ├── rewardType.go ├── upgradeAbilities.go └── wheelRank.go ├── go.mod ├── go.sum ├── helper ├── helper.go └── responses.go ├── inforeporters └── stats.go ├── log └── error.go ├── logic ├── battle │ └── drawBattleRival.go ├── campaign │ └── getWonRewards.go ├── conversion │ ├── configuredCampaignToCampaign.go │ ├── configuredEventToEvent.go │ ├── configuredInfoToInformation.go │ ├── configuredTickerToTicker.go │ ├── playerToBattleData.go │ └── playerToLeaderboardEntry.go ├── finders.go ├── gameplay │ └── requiredItemPayment.go ├── generators.go ├── roulette │ └── randomChaoWheelChoice.go └── wheelRefreshLogic.go ├── main1.go ├── meta └── versions.go ├── muxhandlers ├── battle.go ├── chao.go ├── character.go ├── event.go ├── friend.go ├── game.go ├── leaderboard.go ├── login.go ├── message.go ├── muxobj │ └── objMuxHandler.go ├── option.go ├── player.go ├── raidbossSpin.go ├── sgn.go ├── spin.go └── store.go ├── netobj ├── battleState.go ├── chao.go ├── chaoRouletteGroup.go ├── chaoSpinPrize.go ├── chaoSpinResult.go ├── chaoWheelOptions.go ├── character.go ├── constnetobjs │ ├── allNetChao.go │ └── blankPlayer.go ├── eventState.go ├── loginBonusState.go ├── mileageFriend.go ├── mileageMapState.go ├── optionUserResult.go ├── playCharacter.go ├── player.go ├── playerState.go ├── playerVarious.go ├── raidBossGroup.go ├── raidBossState.go ├── raidbossWheelOptions.go ├── rouletteInfo.go ├── transferCredentials.go ├── userRaidBossState.go └── wheelOptions.go ├── obj ├── battleData.go ├── battlePair.go ├── battleStatus.go ├── campaign.go ├── chao.go ├── chaoPrize.go ├── character.go ├── constobjs │ ├── chao.go │ ├── chapterToMileageReward.go │ ├── character.go │ ├── defaultChaoPrizeWheelPrizeList.go │ ├── defaultCostListConsumedItem.go │ ├── defaultDailyBattlePrizeList.go │ ├── defaultEventRewardList.go │ ├── defaultFirstLoginBonusRewardList.go │ ├── defaultLeagueData.go │ ├── defaultLoginBonusRewardList.go │ ├── defaultMessages.go │ ├── defaultMileageIncentives.go │ ├── defaultPlayerStateItems.go │ ├── defaultRedStarItems.go │ ├── defaultSpinItems.go │ ├── items.go │ ├── shopAmounts.go │ └── shopPrices.go ├── consumedItem.go ├── costItem.go ├── event.go ├── eventReward.go ├── incentive.go ├── information.go ├── item.go ├── leaderboardEntry.go ├── leagueData.go ├── loginBonusReward.go ├── loginBonusStatus.go ├── mapInfo.go ├── message.go ├── messageItem.go ├── mileageIncentive.go ├── mileageReward.go ├── operatorInformation.go ├── operatorMessage.go ├── operatorScore.go ├── present.go ├── raidbossPrize.go ├── raidbossUser.go ├── redStarItem.go ├── requests_generic.go ├── selectReward.go └── ticker.go ├── options └── characterOptions.go ├── orpc ├── base.go └── rpcobj │ ├── config.go │ ├── status.go │ ├── toolbox.go │ ├── toolboxDebug.go │ ├── toolboxGetters.go │ ├── toolboxResetters.go │ └── toolboxSetters.go ├── requests ├── battle.go ├── chao.go ├── character.go ├── event.go ├── friend.go ├── game.go ├── generic.go ├── leaderboard.go ├── login.go ├── message.go ├── player.go ├── raidbossSpin.go ├── sgn.go ├── spin.go └── store.go ├── responses ├── apollo.go ├── baseResponse.go ├── battle.go ├── chao.go ├── character.go ├── event.go ├── friend.go ├── game.go ├── leaderboard.go ├── login.go ├── message.go ├── option.go ├── player.go ├── raidbossSpin.go ├── responseobjs │ ├── baseInfo.go │ └── errorMessage.go ├── sgn.go ├── spin.go └── store.go └── status └── statuses.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: 3 | push: 4 | branches: [master, mysql_testing] 5 | jobs: 6 | 7 | build: 8 | name: Build 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - name: Set up Go 1.18 13 | uses: actions/setup-go@v1 14 | with: 15 | go-version: 1.18 16 | id: go 17 | 18 | - name: Check out code into the Go module directory 19 | uses: actions/checkout@v2 20 | 21 | - name: Get dependencies 22 | run: | 23 | go get -v -t -d ./... 24 | if [ -f Gopkg.toml ]; then 25 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 26 | dep ensure 27 | fi 28 | 29 | - name: Build 30 | run: go build -v . 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.db.old 3 | *.old 4 | *.bat 5 | 6 | *.json 7 | 8 | logging/ 9 | 10 | # Created by https://www.gitignore.io/api/go 11 | # Edit at https://www.gitignore.io/?templates=go 12 | 13 | ### Go ### 14 | # Binaries for programs and plugins 15 | *.exe 16 | *.exe~ 17 | *.dll 18 | *.so 19 | *.dylib 20 | 21 | # Test binary, built with `go test -c` 22 | *.test 23 | 24 | # Output of the go coverage tool, specifically when used with LiteIDE 25 | *.out 26 | 27 | # Dependency directories (remove the comment below to include it) 28 | # vendor/ 29 | 30 | ### Go Patch ### 31 | /vendor/ 32 | /Godeps/ 33 | 34 | # End of https://www.gitignore.io/api/go 35 | 36 | # Created by https://www.gitignore.io/api/git 37 | # Edit at https://www.gitignore.io/?templates=git 38 | 39 | ### Git ### 40 | # Created by git for backups. To disable backups in Git: 41 | # $ git config --global mergetool.keepBackup false 42 | *.orig 43 | 44 | # Created by git when using merge tools for conflicts 45 | *.BACKUP.* 46 | *.BASE.* 47 | *.LOCAL.* 48 | *.REMOTE.* 49 | *_BACKUP_*.txt 50 | *_BASE_*.txt 51 | *_LOCAL_*.txt 52 | *_REMOTE_*.txt 53 | 54 | # End of https://www.gitignore.io/api/git 55 | 56 | # Compiled source # 57 | ################### 58 | *.com 59 | *.class 60 | *.dll 61 | *.exe 62 | *.o 63 | *.so 64 | 65 | # Packages # 66 | ############ 67 | # it's better to unpack these files and commit the raw source 68 | # git has its own built in compression methods 69 | *.7z 70 | *.dmg 71 | *.gz 72 | *.iso 73 | *.jar 74 | *.rar 75 | *.tar 76 | *.zip 77 | 78 | # Logs and databases # 79 | ###################### 80 | *.log 81 | *.sql 82 | *.sqlite 83 | 84 | # OS generated files # 85 | ###################### 86 | .DS_Store 87 | .DS_Store? 88 | ._* 89 | .Spotlight-V100 90 | .Trashes 91 | ehthumbs.db 92 | Thumbs.db 93 | 94 | # Linux binary 95 | outrun 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Outrun for Revival 2 | 3 | **THIS BRANCH IS NOT INTENDED TO BE SELF-HOSTED. This version of Outrun for Revival is only designed to work with 2.2.0. For a version of Outrun for Revival which is self-hostable, go [here](https://github.com/RunnersRevival/outrun/tree/self-hostable) instead.** 4 | 5 | ### Summary 6 | 7 | Outrun for Revival is a fork of Outrun, a custom server for Sonic Runners reverse engineered from the [Sonic Runners Revival](https://sonicrunners.com) project back during the Open Beta. It is intended for use on the Sonic Runners Revival server, but can be used for your own private servers as well. 8 | 9 | ### Current functionality 10 | 11 | Notable: 12 | - Timed Mode 13 | - Story Mode 14 | - Ring/Red Star Ring keeping 15 | - Functional shop 16 | - Character/Chao equipping 17 | - Character leveling and progression 18 | - Item/Chao roulette functionality 19 | - Events 20 | - Campaigns 21 | - Basic ranking 22 | - Login Bonuses 23 | - Daily Challenge 24 | - Daily Battles 25 | 26 | Functional: 27 | - Android and iOS support 28 | - High score keeping 29 | - In game notices 30 | - Deep configuration options 31 | - Powerful RPC control functions 32 | - Ticker notices 33 | - Low CPU usage 34 | - Analytics support 35 | - Revive Token keeping 36 | 37 | ### Building 38 | 39 | 1. [Download and install Go 1.18](https://golang.org/dl/) 40 | 2. [Download and install Git](https://git-scm.com/downloads) (for `go get`) 41 | 3. Set your [GOPATH](https://github.com/golang/go/wiki/SettingGOPATH) environment variable (on Windows, the default is `C:\Users\UsernameHere\go`, while on Linux the default is `/home/usernamehere/go` - only change this if you want to put your work in another directory) 42 | 4. Open a terminal/command prompt 43 | 5. Use `cd` ([Windows,](https://www.digitalcitizen.life/command-prompt-how-use-basic-commands) [Linux/macOS](https://www.macworld.com/article/2042378/master-the-command-line-navigating-files-and-folders.html)) to navigate to a directory of choice 44 | 6. Run `go get github.com/RunnersRevival/outrun` and wait until the command line returns 45 | 7. Run `go build github.com/RunnersRevival/outrun` and wait until the build is complete 46 | 8. Run the produced executable (`outrun.exe` on Windows, `outrun` on Linux/macOS) 47 | 48 | The latest binaries for **Outrun for Revival** can be found [in the releases tab.](https://github.com/RunnersRevival/outrun/releases) 49 | 50 | #### Notice regarding self-hosting 51 | 52 | This branch of the Outrun for Revival code is not designed to be self-hosted. You will not be able to use 2.0.3 with this codebase, and as such we do not provide patching instructions for iOS and Android in this branch. 53 | 54 | ### Misc. 55 | 56 | Any pull requests deemed code improvements are strongly encouraged. Refactors may be merged into a different branch. 57 | 58 | ### Credits 59 | 60 | Much thanks to: 61 | - **YPwn**, whose closest point of online social contact I do not know, for creating and running the Sonic Runners Revival server upon which this project based much of its code upon. 62 | - **[@Sazpaimon](https://github.com/Sazpaimon)** for finding the encryption key I so desparately looked for but could not on my own. 63 | - **nacabaro** (nacabaro#2138 on Discord) for traffic logging and the discovery of **[DaGuAr](https://www.youtube.com/user/Gorila5)**'s asset archive. 64 | 65 | #### Additional assistance 66 | - Story Mode items 67 | - lukaafx (Discord @Kalu04#3243) 68 | - [TemmieFlakes](https://twitter.com/pictochat3) 69 | - SuperSonic893YT 70 | -------------------------------------------------------------------------------- /analytics/anobj/anobj.go: -------------------------------------------------------------------------------- 1 | package anobj 2 | 3 | import "time" 4 | 5 | type Aggret struct { // "aggregator" 6 | PlayerID string `json:"playerID"` 7 | CreationDate int64 `json:"creationDate"` 8 | Data []int64 `json:"data"` 9 | HistoricalStoryScores []int64 `json:"historicalStoryScores"` 10 | HistoricalTimedScores []int64 `json:"historicalTimedScores"` 11 | LoginTimes []int64 `json:"loginTimes"` 12 | } 13 | 14 | func NewAggret(pid string) Aggret { 15 | return Aggret{ 16 | pid, 17 | time.Now().UTC().Unix(), 18 | make([]int64, 75), // must be longer than length of factors.AnalyticType* 19 | make([]int64, 20), 20 | make([]int64, 20), 21 | []int64{}, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /analytics/collector.go: -------------------------------------------------------------------------------- 1 | package analytics 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "time" 7 | 8 | "github.com/RunnersRevival/outrun/analytics/anobj" 9 | "github.com/RunnersRevival/outrun/analytics/factors" 10 | "github.com/RunnersRevival/outrun/config" 11 | "github.com/RunnersRevival/outrun/consts" 12 | "github.com/RunnersRevival/outrun/db/dbaccess" 13 | ) 14 | 15 | func Store(pid string, atype int, value ...int64) (bool, error) { 16 | if !config.CFile.EnableAnalytics { 17 | return true, nil 18 | } 19 | if config.CFile.DebugPrints { 20 | log.Println("analytics.Store", pid, atype, value) 21 | } 22 | found := true 23 | data, err := dbaccess.Get(consts.DBBucketAnalytics, pid) 24 | var aggret anobj.Aggret 25 | if err != nil { 26 | // player isn't found, so make a new Aggret 27 | log.Println("analytics.Store warning: error (", err.Error(), "), using default") 28 | aggret = anobj.NewAggret(pid) 29 | found = false 30 | } else { 31 | if config.CFile.DebugPrints { 32 | log.Println("analytics.Store fetched data: ", string(data)) 33 | } 34 | if len(data) != 0 { // there's data here 35 | err = json.Unmarshal(data, &aggret) 36 | if err != nil { 37 | return found, err 38 | } 39 | } else { 40 | log.Println("analytics.Store warning: data found, but empty, using default") 41 | aggret = anobj.NewAggret(pid) 42 | found = false 43 | } 44 | } 45 | 46 | if atype == factors.AnalyticTypeAverageStoryScore { 47 | // add score to historical scores for averaging 48 | aggret.HistoricalStoryScores = append(aggret.HistoricalStoryScores, value[0])[1:] 49 | // average scores 50 | total := int64(0) 51 | count := 0 52 | for _, score := range aggret.HistoricalStoryScores { 53 | if score > 0 { 54 | count++ 55 | total += score 56 | } 57 | } 58 | avg := total / int64(count) 59 | aggret.Data[atype] = avg 60 | } else if atype == factors.AnalyticTypeAverageTimedScore { 61 | // add score to historical scores for averaging 62 | aggret.HistoricalTimedScores = append(aggret.HistoricalTimedScores, value[0])[1:] 63 | // average scores 64 | total := int64(0) 65 | count := 0 66 | for _, score := range aggret.HistoricalTimedScores { 67 | if score > 0 { 68 | count++ 69 | total += score 70 | } 71 | } 72 | avg := total / int64(count) 73 | aggret.Data[atype] = avg 74 | } else if atype == factors.AnalyticTypeLogins { 75 | aggret.Data[atype]++ 76 | aggret.LoginTimes = append(aggret.LoginTimes, time.Now().UTC().Unix()) 77 | } else { 78 | if len(value) == 0 { 79 | aggret.Data[atype]++ 80 | } else { 81 | aggret.Data[atype] += value[0] 82 | } 83 | } 84 | 85 | jdata, err := json.Marshal(aggret) 86 | if err != nil { 87 | return found, err 88 | } 89 | err = dbaccess.Set(consts.DBBucketAnalytics, pid, jdata) 90 | if err != nil { 91 | return found, err 92 | } 93 | 94 | return found, nil 95 | } 96 | 97 | func Get(pid string, atype int) (int64, bool, error) { 98 | found := true 99 | data, err := dbaccess.Get(consts.DBBucketAnalytics, pid) 100 | var aggret anobj.Aggret 101 | if err != nil { 102 | // player isn't found, so make a new Aggret 103 | aggret = anobj.NewAggret(pid) 104 | found = false 105 | } 106 | err = json.Unmarshal(data, &aggret) 107 | if err != nil { 108 | return 0, found, err 109 | } 110 | return aggret.Data[atype], found, nil 111 | } 112 | -------------------------------------------------------------------------------- /analytics/factors/factors.go: -------------------------------------------------------------------------------- 1 | package factors 2 | 3 | const ( 4 | AnalyticTypeLogins = iota // fulfilled 5 | AnalyticTypeStoryStarts // fulfilled 6 | AnalyticTypeStoryEnds // fulfilled 7 | AnalyticTypeTimedStarts // fulfilled 8 | AnalyticTypeTimedEnds // fulfilled 9 | AnalyticTypePurchaseRings // fulfilled 10 | AnalyticTypePurchaseEnergy // fulfilled 11 | AnalyticTypePurchaseRedRings // Placeholder! 12 | AnalyticTypeSpinItemRoulette // fulfilled 13 | AnalyticTypeSpinChaoRoulette // fulfilled 14 | AnalyticTypeChangeMainCharacter // fulfilled 15 | AnalyticTypeChangeSubCharacter // fulfilled 16 | AnalyticTypeChangeMainChao // fulfilled 17 | AnalyticTypeChangeSubChao // fulfilled 18 | AnalyticTypeAverageStoryScore 19 | AnalyticTypeAverageTimedScore 20 | AnalyticTypeSpendRings 21 | AnalyticTypeSpendRedRings 22 | AnalyticTypeRevives // fulfilled 23 | AnalyticTypeBadRequests 24 | ) 25 | -------------------------------------------------------------------------------- /bgtasks/purgeSessionIDs.go: -------------------------------------------------------------------------------- 1 | package bgtasks 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/db" 7 | ) 8 | 9 | func PurgeSessionIDs() { 10 | for true { 11 | time.Sleep(10 * time.Minute) 12 | db.PurgeAllExpiredSessionIDs() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bgtasks/touchAnalyticsDB.go: -------------------------------------------------------------------------------- 1 | package bgtasks 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/RunnersRevival/outrun/consts" 7 | "github.com/RunnersRevival/outrun/db/dbaccess" 8 | ) 9 | 10 | func TouchAnalyticsDB() { 11 | err := dbaccess.Set(consts.DBBucketAnalytics, "touch", []byte{}) 12 | if err != nil { 13 | log.Println("[ERR] Unable to touch " + consts.DBBucketAnalytics + ": " + err.Error()) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config/authorizedconf/authorizedParser.go: -------------------------------------------------------------------------------- 1 | package authorizedconf 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | ) 8 | 9 | var Defaults = map[string]interface{}{ 10 | "DAuthorizedIDs": []string{}, 11 | "DAllowCustomBlockMessage": false, 12 | "DCustomBlockMessage": "", 13 | } 14 | 15 | type ConfigFile struct { 16 | AuthorizedIDs []string `json:"ids,omitempty"` 17 | AllowCustomBlockMessage bool `json:"useCustomBlockMessage,omitempty"` 18 | CustomBlockMessage string `json:"customBlockMessage,omitempty"` 19 | } 20 | 21 | var CFile ConfigFile 22 | 23 | func Parse(filename string) error { 24 | CFile = ConfigFile{ 25 | Defaults["DAuthorizedIDs"].([]string), 26 | Defaults["DAllowCustomBlockMessage"].(bool), 27 | Defaults["DCustomBlockMessage"].(string), 28 | } 29 | file, err := loadFile(filename) 30 | if err != nil { 31 | return err 32 | } 33 | err = json.Unmarshal(file, &CFile) 34 | if err != nil { 35 | log.Fatal("Error during Unmarshal(): ", err) 36 | } 37 | return nil 38 | } 39 | 40 | func loadFile(filename string) ([]byte, error) { 41 | b, err := ioutil.ReadFile(filename) 42 | if err != nil { 43 | return []byte{}, err 44 | } 45 | return b, err 46 | } -------------------------------------------------------------------------------- /config/campaignconf/campaignconfParser.go: -------------------------------------------------------------------------------- 1 | package campaignconf 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | 8 | "github.com/RunnersRevival/outrun/enums" 9 | ) 10 | 11 | // defaults 12 | // default variable names MUST be "D" + (nameOfVariable) 13 | var Defaults = map[string]interface{}{ 14 | "DAllowCampaigns": false, 15 | "DCurrentCampaigns": []ConfiguredCampaign{}, 16 | } 17 | 18 | var CampaignTypes = map[string]int64{ 19 | "bankedRingBonus": enums.CampaignTypeBankedRingBonus, 20 | "chaoRouletteCost": enums.CampaignTypeChaoRouletteCost, 21 | "characterUpgradeCost": enums.CampaignTypeCharacterUpgradeCost, 22 | "continueCost": enums.CampaignTypeContinueCost, 23 | "dailyMissionBonus": enums.CampaignTypeDailyMissionBonus, 24 | "freeWheelSpinCount": enums.CampaignTypeFreeWheelSpinCount, 25 | "gameItemCost": enums.CampaignTypeGameItemCost, 26 | "inviteCount": enums.CampaignTypeInviteCount, 27 | "jackpotValue": enums.CampaignTypeJackpotValueBonus, 28 | "mileagePassingRingBonus": enums.CampaignTypeMileagePassingRingBonus, 29 | "premiumRouletteOdds": enums.CampaignTypePremiumRouletteOdds, 30 | "purchaseAddEnergy": enums.CampaignTypePurchaseAddEnergies, 31 | "purchaseAddRaidEnergy": enums.CampaignTypePurchaseAddRaidEnergies, 32 | "purchaseAddRedRings": enums.CampaignTypePurchaseAddRedRings, 33 | "purchaseAddRedRingsNoChargeUser": enums.CampaignTypePurchaseAddRedRingsNoChargeUser, 34 | "purchaseAddRings": enums.CampaignTypePurchaseAddRings, 35 | "sendAddEnergy": enums.CampaignTypeSendAddEnergies, 36 | } 37 | 38 | type ConfiguredCampaign struct { 39 | Type string `json:"type"` 40 | Content int64 `json:"content"` 41 | SubContent int64 `json:"subContent"` 42 | StartTime int64 `json:"startTime"` // * 43 | EndTime int64 `json:"endTime"` // * 44 | } 45 | 46 | func (c ConfiguredCampaign) RealType() int64 { 47 | return CampaignTypes[c.Type] 48 | } 49 | func (c ConfiguredCampaign) HasValidType() bool { 50 | _, ok := CampaignTypes[c.Type] 51 | return ok 52 | } 53 | 54 | var CFile ConfigFile 55 | 56 | type ConfigFile struct { 57 | AllowCampaigns bool `json:"allowCampaigns,omitempty"` 58 | CurrentCampaigns []ConfiguredCampaign `json:"currentCampaigns,omitempty"` 59 | } 60 | 61 | func Parse(filename string) error { 62 | CFile = ConfigFile{ 63 | Defaults["DAllowCampaigns"].(bool), 64 | Defaults["DCurrentCampaigns"].([]ConfiguredCampaign), 65 | } 66 | file, err := loadFile(filename) 67 | if err != nil { 68 | return err 69 | } 70 | err = json.Unmarshal(file, &CFile) 71 | if err != nil { 72 | return err 73 | } 74 | newCampaigns := []ConfiguredCampaign{} 75 | for i, cc := range CFile.CurrentCampaigns { 76 | if !cc.HasValidType() { 77 | log.Printf("[WARN] Invalid campaign type %s at index %v, ignoring\n", cc.Type, i) 78 | continue 79 | } 80 | newCampaigns = append(newCampaigns, cc) 81 | } 82 | CFile.CurrentCampaigns = newCampaigns 83 | return nil 84 | } 85 | 86 | func loadFile(filename string) ([]byte, error) { 87 | b, err := ioutil.ReadFile(filename) 88 | if err != nil { 89 | return []byte{}, err 90 | } 91 | return b, err 92 | } 93 | -------------------------------------------------------------------------------- /config/eventconf/eventconfParser.go: -------------------------------------------------------------------------------- 1 | package eventconf 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | 8 | "github.com/RunnersRevival/outrun/enums" 9 | ) 10 | 11 | // defaults 12 | // default variable names MUST be "D" + (nameOfVariable) 13 | var Defaults = map[string]interface{}{ 14 | "DAllowEvents": false, 15 | "DCurrentEvents": []ConfiguredEvent{}, 16 | "DEnforceGlobal": false, 17 | } 18 | 19 | var EventTypes = map[string]int64{ 20 | "specialStage": enums.EventIDSpecialStage, 21 | "raidBoss": enums.EventIDRaidBoss, 22 | "collectObject": enums.EventIDCollectObject, 23 | "gacha": enums.EventIDGacha, 24 | "advert": enums.EventIDAdvert, 25 | "quick": enums.EventIDQuick, 26 | "bgm": enums.EventIDBGM, 27 | } 28 | 29 | type ConfiguredEvent struct { 30 | ID int64 `json:"id"` 31 | Type string `json:"type"` 32 | StartTime int64 `json:"startTime"` // * 33 | EndTime int64 `json:"endTime"` // * 34 | } 35 | 36 | /* 37 | * StartTime and EndTime can use certain negative values to represent time 38 | functions that are not otherwise easily possible using a static 39 | JSON file. -2 will be the beginning of the system's day, and -3 will 40 | be the end of the system's day. 41 | A value of -4 will activate the event no matter what. For StartTime, it 42 | will use the current time minus 1 in order to say the event has just started. 43 | For EndTime, it will use 24 hours into the future. This isn't foolproof, as 44 | the event could expire and yield odd results, but if you have a player that 45 | is playing for long enough for that to happen, you have a _different_ problem 46 | on your hands. 47 | */ 48 | 49 | func (c ConfiguredEvent) RealType() int64 { 50 | return EventTypes[c.Type] 51 | } 52 | func (c ConfiguredEvent) HasValidType() bool { 53 | _, ok := EventTypes[c.Type] 54 | return ok 55 | } 56 | 57 | var CFile ConfigFile 58 | 59 | type ConfigFile struct { 60 | AllowEvents bool `json:"allowEvents,omitempty"` 61 | CurrentEvents []ConfiguredEvent `json:"currentEvents,omitempty"` 62 | EnforceGlobal bool `json:"enforceGlobal,omitempty"` 63 | } 64 | 65 | func Parse(filename string) error { 66 | CFile = ConfigFile{ 67 | Defaults["DAllowEvents"].(bool), 68 | Defaults["DCurrentEvents"].([]ConfiguredEvent), 69 | Defaults["DEnforceGlobal"].(bool), 70 | } 71 | file, err := loadFile(filename) 72 | if err != nil { 73 | return err 74 | } 75 | err = json.Unmarshal(file, &CFile) 76 | if err != nil { 77 | return err 78 | } 79 | newEvents := []ConfiguredEvent{} 80 | for i, ce := range CFile.CurrentEvents { 81 | if !ce.HasValidType() { 82 | log.Printf("[WARN] Invalid event type %s at index %v, ignoring\n", ce.Type, i) 83 | continue 84 | } 85 | newEvents = append(newEvents, ce) 86 | } 87 | CFile.CurrentEvents = newEvents 88 | return nil 89 | } 90 | 91 | func loadFile(filename string) ([]byte, error) { 92 | b, err := ioutil.ReadFile(filename) 93 | if err != nil { 94 | return []byte{}, err 95 | } 96 | return b, err 97 | } 98 | -------------------------------------------------------------------------------- /config/parser.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | ) 7 | 8 | // defaults 9 | // default variable names MUST be "D" + (nameOfVariable) 10 | var Defaults = map[string]interface{}{ 11 | "DPort": "9001", 12 | "DDoTimeLogging": true, 13 | "DLogUnknownRequests": true, 14 | "DLogAllRequests": false, 15 | "DLogAllResponses": false, 16 | "DDebug": false, 17 | "DDebugPrints": false, 18 | "DEnableRPC": false, 19 | "DRPCPort": "23432", 20 | "DEnablePublicStats": false, 21 | "DEndpointPrefix": "", 22 | "DEnableAnalytics": false, 23 | "DPrintPlayerNames": false, 24 | "DEventConfigFilename": "event_config.json", 25 | "DSilenceEventConfigErrors": false, 26 | "DInfoConfigFilename": "info_config.json", 27 | "DSilenceInfoConfigErrors": false, 28 | "DGameConfigFilename": "game_config.json", 29 | "DSilenceGameConfigErrors": false, 30 | "DCampaignConfigFilename": "campaign_config.json", 31 | "DAuthorizedConfigFilename": "authorized_config.json", 32 | "DSilenceCampaignConfigErrors": false, 33 | "DLegacyCompatibilityMode": false, 34 | "DDisableRegistrations": false, 35 | } 36 | 37 | var CFile ConfigFile 38 | 39 | type ConfigFile struct { 40 | Port string `json:"port,omitempty"` 41 | DoTimeLogging bool `json:"doTimeLogging,omitempty"` 42 | LogUnknownRequests bool `json:"logUnknownRequests,omitempty"` 43 | LogAllRequests bool `json:"logAllRequests,omitempty"` 44 | LogAllResponses bool `json:"logAllResponses,omitempty"` 45 | Debug bool `json:"debug,omitempty"` 46 | DebugPrints bool `json:"debugPrints,omitempty"` 47 | EnableRPC bool `json:"enableRPC,omitempty"` 48 | RPCPort string `json:"rpcPort,omitempty"` 49 | EnablePublicStats bool `json:"enablePublicStats,omitempty"` 50 | EndpointPrefix string `json:"endpointPrefix,omitempty"` 51 | EnableAnalytics bool `json:"enableAnalytics,omitempty"` 52 | PrintPlayerNames bool `json:"printPlayerNames,omitempty"` 53 | EventConfigFilename string `json:"eventConfigFilename,omitempty"` 54 | SilenceEventConfigErrors bool `json:"silenceEventConfigErrors,omitempty"` 55 | InfoConfigFilename string `json:"infoConfigFilename,omitempty"` 56 | SilenceInfoConfigErrors bool `json:"silenceInfoConfigErrors,omitempty"` 57 | GameConfigFilename string `json:"gameConfigFilename,omitempty"` 58 | SilenceGameConfigErrors bool `json:"silenceGameConfigErrors,omitempty"` 59 | CampaignConfigFilename string `json:"campaignConfigFilename,omitempty"` 60 | AuthorizedConfigFilename string `json:"authorizedConfigFilename,omitempty"` 61 | SilenceCampaignConfigErrors bool `json:"silenceCampaignConfigErrors,omitempty"` 62 | LegacyCompatibilityMode bool `json:"legacyCompatibilityMode,omitempty"` // disables the version check 63 | DisableRegistrations bool `json:"disableRegistrations,omitempty"` // disables the LoginAlpha path 64 | } 65 | 66 | func Parse(filename string) error { 67 | CFile = ConfigFile{ 68 | Defaults["DPort"].(string), 69 | Defaults["DDoTimeLogging"].(bool), 70 | Defaults["DLogUnknownRequests"].(bool), 71 | Defaults["DLogAllRequests"].(bool), 72 | Defaults["DLogAllResponses"].(bool), 73 | Defaults["DDebug"].(bool), 74 | Defaults["DDebugPrints"].(bool), 75 | Defaults["DEnableRPC"].(bool), 76 | Defaults["DRPCPort"].(string), 77 | Defaults["DEnablePublicStats"].(bool), 78 | Defaults["DEndpointPrefix"].(string), 79 | Defaults["DEnableAnalytics"].(bool), 80 | Defaults["DPrintPlayerNames"].(bool), 81 | Defaults["DEventConfigFilename"].(string), 82 | Defaults["DSilenceEventConfigErrors"].(bool), 83 | Defaults["DInfoConfigFilename"].(string), 84 | Defaults["DSilenceInfoConfigErrors"].(bool), 85 | Defaults["DGameConfigFilename"].(string), 86 | Defaults["DSilenceGameConfigErrors"].(bool), 87 | Defaults["DCampaignConfigFilename"].(string), 88 | Defaults["DAuthorizedConfigFilename"].(string), 89 | Defaults["DSilenceCampaignConfigErrors"].(bool), 90 | Defaults["DLegacyCompatibilityMode"].(bool), 91 | Defaults["DDisableRegistrations"].(bool), 92 | } 93 | file, err := loadFile(filename) 94 | if err != nil { 95 | return err 96 | } 97 | err = json.Unmarshal(file, &CFile) 98 | if err != nil { 99 | return err 100 | } 101 | return nil 102 | } 103 | 104 | func loadFile(filename string) ([]byte, error) { 105 | b, err := ioutil.ReadFile(filename) 106 | if err != nil { 107 | return []byte{}, err 108 | } 109 | return b, err 110 | } 111 | -------------------------------------------------------------------------------- /consts/campaignPointScores.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | var PointScores = map[int64][]int64{ // chapterNum: []int{...} 4 | //1: []int64{1000, 2000, 3000, 4000, 5000, -1}, // last element is the boss fight point, should never encounter 5 | //2: []int64{50000, 75000, 100000, 125000, -1}, 6 | 1: []int64{1000, 1000, 1000, 1000, 1000, -1}, // last element is the boss fight point, should never encounter 7 | 2: []int64{50000, 50000, 50000, 50000, 50000, -1}, 8 | 3: []int64{50000, 50000, 50000, 50000, 50000, -1}, 9 | 4: []int64{65000, 65000, 65000, 65000, 65000, -1}, 10 | 5: []int64{65000, 65000, 65000, 65000, 65000, -1}, //This one get glitched sometimes with no apparent reason -F 11 | 6: []int64{65000, 65000, 65000, 65000, 65000, -1}, 12 | 7: []int64{110000, 110000, 110000, 110000, 110000, -1}, 13 | 8: []int64{110000, 110000, 110000, 110000, 110000, -1}, 14 | 9: []int64{160000, 160000, 160000, 160000, 160000, -1}, 15 | 10: []int64{160000, 160000, 160000, 160000, 160000, -1}, 16 | 11: []int64{260000, 260000, 260000, 260000, 260000, -1}, 17 | 12: []int64{390000, 390000, 390000, 390000, 390000, -1}, 18 | 13: []int64{420000, 420000, 420000, 420000, 420000, -1}, 19 | 14: []int64{420000, 420000, 420000, 420000, 420000, -1}, 20 | 15: []int64{420000, 420000, 420000, 420000, 420000, -1}, 21 | 16: []int64{600000, 600000, 600000, 600000, 600000, -1}, 22 | 17: []int64{1200000, 1200000, 1200000, 1200000, 1200000, -1}, 23 | 18: []int64{1280000, 1280000, 1280000, 1280000, 1280000, -1}, 24 | 19: []int64{1600000, 1600000, 1600000, 1600000, 1600000, -1}, 25 | 20: []int64{1700000, 1700000, 1700000, 1700000, 1700000, -1}, 26 | 21: []int64{2040000, 2040000, 2040000, 2040000, 2040000, -1}, 27 | 22: []int64{2160000, 2160000, 2160000, 2160000, 2160000, -1}, 28 | 23: []int64{2280000, 2280000, 2280000, 2280000, 2280000, -1}, 29 | 24: []int64{2800000, 2800000, 2800000, 2800000, 2800000, -1}, 30 | 25: []int64{2280000, 2280000, 2280000, 2280000, 2280000, -1}, //WIP 31 | 32 | } 33 | -------------------------------------------------------------------------------- /consts/chaoRouletteConsts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | ChaoRouletteRedRingCost = int64(15) 5 | ChaoRouletteTicketCost = int64(1) 6 | ChaoRouletteCharacterStarIncrease = int64(1) 7 | ChaoRouletteCharacterLevelIncrease = int64(1) 8 | ChaoRouletteChaoLevelIncreaseLow = int64(1) 9 | ChaoRouletteChaoLevelIncreaseHigh = int64(1) 10 | ) 11 | -------------------------------------------------------------------------------- /consts/characterUpgradeValues.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | var UpgradeIncreases = map[string]int64{ 4 | "300000": 250, 5 | "300001": 250, 6 | "300002": 250, 7 | "300003": 300, 8 | "300004": 450, 9 | "300005": 500, 10 | "300006": 500, 11 | "300007": 600, 12 | "300008": 650, 13 | "300009": 700, 14 | "300010": 600, 15 | "300011": 600, 16 | "300012": 650, 17 | "300013": 750, 18 | "300014": 850, 19 | "300015": 950, 20 | "300016": 800, 21 | "300017": 700, 22 | "300018": 1050, 23 | "300019": 1500, 24 | "300020": 2250, 25 | "300021": 1050, // Marine 26 | "300022": 700, // Tangle 27 | "300023": 700, // Whisper 28 | "301000": 500, 29 | "301001": 500, 30 | "301002": 500, 31 | "301003": 500, 32 | "301004": 500, 33 | "301005": 500, 34 | "301006": 500, 35 | "301007": 500, 36 | "301008": 700, 37 | } 38 | 39 | const () 40 | -------------------------------------------------------------------------------- /consts/dailyMissionRewards.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import "github.com/RunnersRevival/outrun/enums" 4 | 5 | // Change these to alter the item types and counts for each day 6 | var DailyMissionRewards = []int64{ 7 | enums.ItemIDRing, 8 | enums.ItemIDRedRing, 9 | enums.ItemIDRing, 10 | enums.ItemIDRedRing, 11 | enums.ItemIDRing, 12 | enums.ItemIDRedRing, 13 | enums.ItemIDRedRing, 14 | } 15 | var DailyMissionRewardCounts = []int64{ 16 | 1000, 17 | 10, 18 | 5000, 19 | 20, 20 | 10000, 21 | 30, 22 | 60, 23 | } 24 | -------------------------------------------------------------------------------- /consts/db.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | DBFileName = "outrun.db" 5 | DBBucketSessionIDs = "sessionIDs" 6 | DBBucketPlayers = "players" 7 | DBBucketAnalytics = "analytics" 8 | DBBucketTransferCreds = "transferCreds" 9 | DBSessionExpiryTime = 5600 10 | ) 11 | 12 | const ( 13 | BattleDBFileName = "battle.db" 14 | BattleDBBucketSessionIDs = "sessionIDs" 15 | BattleDBBucketMatched = "matched" 16 | BattleDBBucketWaiting = "waiting" 17 | BattleDBSessionExpiryTime = 5600 18 | ) 19 | -------------------------------------------------------------------------------- /consts/episodeChapters.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | var EpisodeWithChapters = map[int64]int64{ 4 | 6: 2, 5 | 11: 2, 6 | 16: 2, 7 | 19: 2, 8 | 20: 2, 9 | 22: 2, 10 | 23: 2, 11 | 24: 2, 12 | 29: 2, 13 | 31: 2, 14 | 33: 2, 15 | 36: 2, 16 | 38: 2, 17 | 39: 2, 18 | 40: 3, 19 | 41: 3, 20 | 42: 2, 21 | 43: 2, 22 | 44: 2, 23 | 46: 2, 24 | 47: 2, 25 | 48: 2, 26 | 49: 2, 27 | 50: 5, 28 | } 29 | -------------------------------------------------------------------------------- /consts/gameVersionTables.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | // lookup table for asset versions; currently unused 4 | var DataVersionForGameVersion = map[string]string{ 5 | "0.0.5": "001", 6 | "1.0.0": "025", // ACTUAL ASSET VERSION UNKNOWN! 7 | "1.0.1": "027", // ACTUAL ASSET VERSION UNKNOWN! 8 | "1.0.2": "029", // ACTUAL ASSET VERSION UNKNOWN! 9 | "1.0.4": "031", // ACTUAL ASSET VERSION UNKNOWN! 10 | "1.1.0": "033", // ACTUAL ASSET VERSION UNKNOWN! 11 | "1.1.1": "035", // ACTUAL ASSET VERSION UNKNOWN! 12 | "1.1.2": "037", // ACTUAL ASSET VERSION UNKNOWN! 13 | "1.1.3": "039", // ACTUAL ASSET VERSION UNKNOWN! 14 | "1.1.4": "041", 15 | "2.0.0": "043", 16 | "2.0.1": "047", 17 | "2.0.3": "050", 18 | "2.1.0": "051", 19 | "2.1.1": "052", 20 | "2.2.0": "053", 21 | "2.2.1": "054", 22 | "2.2.2": "054", 23 | "2.2.3": "054", 24 | } 25 | -------------------------------------------------------------------------------- /consts/itemCosts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import "github.com/RunnersRevival/outrun/enums" 4 | 5 | var ItemPrices = map[string]int64{ 6 | enums.ItemIDStrInvincible: 3000, 7 | enums.ItemIDStrBarrier: 4000, 8 | enums.ItemIDStrMagnet: 3000, 9 | enums.ItemIDStrTrampoline: 2000, 10 | enums.ItemIDStrCombo: 5000, 11 | enums.ItemIDStrLaser: 2000, 12 | enums.ItemIDStrDrill: 2000, 13 | enums.ItemIDStrAsteroid: 1000, 14 | 15 | enums.ItemIDStrBoostScore: 5000, 16 | enums.ItemIDStrBoostTrampoline: 2000, 17 | enums.ItemIDStrBoostSubChara: 3000, 18 | } 19 | -------------------------------------------------------------------------------- /consts/numValues.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | CharacterUpgradeIncrease = 5500 5 | ) 6 | -------------------------------------------------------------------------------- /consts/randomItemList.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "github.com/RunnersRevival/outrun/enums" 7 | ) 8 | 9 | type AmountRange struct { 10 | Min int64 11 | Max int64 12 | Step int64 13 | } 14 | 15 | func (a AmountRange) GetRandom() int64 { 16 | // construct random list first 17 | randomSelections := []int64{} 18 | diff := int64(0) 19 | currMin := a.Min 20 | for diff >= 0 { 21 | randomSelections = append(randomSelections, currMin) 22 | currMin += a.Step 23 | diff = a.Max - currMin 24 | } 25 | selectionIndex := rand.Intn(len(randomSelections)) 26 | selection := randomSelections[selectionIndex] 27 | return selection 28 | } 29 | 30 | // The game does not support RingBonus, DistanceBonus, 31 | // or AnimalBonus on the normal wheel. 32 | 33 | // NOTE: If you remove an item from NormalWheelItemAmountRange 34 | // but don't remove it from RandomItemListNormalWheel, you're going 35 | // to create a memory leak. 36 | // Same thing goes for the big and super variants as well! 37 | 38 | var RandomItemListNormalWheel = []string{ 39 | enums.ItemIDStrInvincible, 40 | enums.ItemIDStrBarrier, 41 | enums.ItemIDStrMagnet, 42 | enums.ItemIDStrTrampoline, 43 | enums.ItemIDStrCombo, 44 | enums.ItemIDStrLaser, 45 | enums.ItemIDStrDrill, 46 | enums.ItemIDStrAsteroid, 47 | enums.ItemIDStrRedRing, 48 | enums.ItemIDStrRing, 49 | //strconv.Itoa(enums.IDTypeItemRouletteWin), 50 | } 51 | 52 | var NormalWheelItemAmountRange = map[string]AmountRange{ 53 | enums.ItemIDStrInvincible: AmountRange{1, 5, 1}, 54 | enums.ItemIDStrBarrier: AmountRange{1, 5, 1}, 55 | enums.ItemIDStrMagnet: AmountRange{1, 5, 1}, 56 | enums.ItemIDStrTrampoline: AmountRange{1, 5, 1}, 57 | enums.ItemIDStrCombo: AmountRange{1, 5, 1}, 58 | enums.ItemIDStrLaser: AmountRange{1, 5, 1}, 59 | enums.ItemIDStrDrill: AmountRange{1, 5, 1}, 60 | enums.ItemIDStrAsteroid: AmountRange{1, 5, 1}, 61 | enums.ItemIDStrRedRing: AmountRange{5, 25, 5}, 62 | enums.ItemIDStrRing: AmountRange{500, 2500, 500}, 63 | //strconv.Itoa(enums.IDTypeItemRouletteWin): AmountRange{1, 1, 1}, 64 | } 65 | 66 | var RandomItemListBigWheel = []string{ 67 | enums.ItemIDStrInvincible, 68 | enums.ItemIDStrBarrier, 69 | enums.ItemIDStrMagnet, 70 | enums.ItemIDStrTrampoline, 71 | enums.ItemIDStrCombo, 72 | enums.ItemIDStrLaser, 73 | enums.ItemIDStrDrill, 74 | enums.ItemIDStrAsteroid, 75 | enums.ItemIDStrRedRing, 76 | enums.ItemIDStrRing, 77 | //strconv.Itoa(enums.IDTypeItemRouletteWin), 78 | } 79 | 80 | var BigWheelItemAmountRange = map[string]AmountRange{ 81 | enums.ItemIDStrInvincible: AmountRange{5, 10, 1}, 82 | enums.ItemIDStrBarrier: AmountRange{5, 10, 1}, 83 | enums.ItemIDStrMagnet: AmountRange{5, 10, 1}, 84 | enums.ItemIDStrTrampoline: AmountRange{5, 10, 1}, 85 | enums.ItemIDStrCombo: AmountRange{5, 10, 1}, 86 | enums.ItemIDStrLaser: AmountRange{5, 10, 1}, 87 | enums.ItemIDStrDrill: AmountRange{5, 10, 1}, 88 | enums.ItemIDStrAsteroid: AmountRange{5, 10, 1}, 89 | enums.ItemIDStrRedRing: AmountRange{10, 50, 10}, 90 | enums.ItemIDStrRing: AmountRange{2500, 5000, 500}, 91 | //strconv.Itoa(enums.IDTypeItemRouletteWin): AmountRange{1, 1, 1}, 92 | } 93 | 94 | var RandomItemListSuperWheel = []string{ 95 | enums.ItemIDStrInvincible, 96 | enums.ItemIDStrBarrier, 97 | enums.ItemIDStrMagnet, 98 | enums.ItemIDStrTrampoline, 99 | enums.ItemIDStrCombo, 100 | enums.ItemIDStrLaser, 101 | enums.ItemIDStrDrill, 102 | enums.ItemIDStrAsteroid, 103 | enums.ItemIDStrRedRing, 104 | enums.ItemIDStrRing, 105 | //strconv.Itoa(enums.IDTypeItemRouletteWin), 106 | } 107 | 108 | var SuperWheelItemAmountRange = map[string]AmountRange{ 109 | enums.ItemIDStrInvincible: AmountRange{10, 20, 2}, 110 | enums.ItemIDStrBarrier: AmountRange{10, 20, 2}, 111 | enums.ItemIDStrMagnet: AmountRange{10, 20, 2}, 112 | enums.ItemIDStrTrampoline: AmountRange{10, 20, 2}, 113 | enums.ItemIDStrCombo: AmountRange{10, 20, 2}, 114 | enums.ItemIDStrLaser: AmountRange{10, 20, 2}, 115 | enums.ItemIDStrDrill: AmountRange{10, 20, 2}, 116 | enums.ItemIDStrAsteroid: AmountRange{10, 20, 2}, 117 | enums.ItemIDStrRedRing: AmountRange{20, 160, 20}, 118 | enums.ItemIDStrRing: AmountRange{5000, 10000, 1000}, 119 | //strconv.Itoa(enums.IDTypeItemRouletteWin): AmountRange{1, 1, 1}, 120 | } 121 | -------------------------------------------------------------------------------- /consts/rouletteConsts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | /* 5 | RouletteJackpotRings = int64(78000) 6 | RouletteFreeSpins = int64(5) 7 | */ 8 | RouletteJackpotRings = int64(185000) 9 | RouletteFreeSpins = int64(3) 10 | ) 11 | -------------------------------------------------------------------------------- /cryption/basic.go: -------------------------------------------------------------------------------- 1 | package cryption 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "regexp" 7 | ) 8 | 9 | var EncryptionIv = []byte("") 10 | var EncryptionKey = []byte("Ec7bLaTdSuXuf5pW") 11 | 12 | func CleanBytes(b []byte) []byte { 13 | re := regexp.MustCompile("[^\u0020-\u007f]+") 14 | return []byte(re.ReplaceAllLiteralString(string(b), "")) 15 | } 16 | 17 | func GetReceivedMessage(r *http.Request) ([]byte, error) { 18 | err := r.ParseForm() 19 | if err != nil { 20 | log.Println("[ERR] Error in parsing form: " + err.Error()) 21 | } 22 | param := r.Form.Get("param") 23 | iv := r.Form.Get("key") 24 | secure := r.Form.Get("secure") 25 | if secure != "1" { 26 | return []byte(param), nil // message is not encrypted! 27 | } 28 | defer func() { 29 | if rErr := recover(); rErr != nil { 30 | log.Println("[ERR] Panic during decryption", rErr) 31 | } 32 | }() 33 | 34 | EncryptionIv = []byte(iv) 35 | paramUnB64, err := B64Decode(param) 36 | if err != nil { 37 | log.Println("[ERR] Couldn't decode param: " + err.Error()) 38 | return []byte(param), err 39 | } 40 | decrypted := Decrypt(paramUnB64, EncryptionKey, EncryptionIv) 41 | decrypted = CleanBytes(decrypted) 42 | return decrypted, nil 43 | } -------------------------------------------------------------------------------- /cryption/cryptid.go: -------------------------------------------------------------------------------- 1 | package cryption 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "encoding/base64" 8 | ) 9 | 10 | func B64Decode(s string) ([]byte, error) { 11 | result, err := base64.StdEncoding.DecodeString(s) 12 | return result, err 13 | } 14 | 15 | func B64Encode(b []byte) string { 16 | result := base64.StdEncoding.EncodeToString(b) 17 | return result 18 | } 19 | 20 | func Decrypt(content, key, iv []byte) []byte { 21 | block, err := aes.NewCipher(key) // get cipher block from key 22 | if err != nil { 23 | panic(err) 24 | } 25 | cbcD := cipher.NewCBCDecrypter(block, iv) // create cbc decrypter 26 | cbcD.CryptBlocks(content, content) // decrypt content 27 | return content 28 | } 29 | 30 | func Encrypt(content, key, iv []byte) []byte { 31 | block, err := aes.NewCipher(key) 32 | if err != nil { 33 | panic(err) 34 | } 35 | content = PKCS5Padding(content, block.BlockSize()) 36 | cbcE := cipher.NewCBCEncrypter(block, iv) 37 | cbcE.CryptBlocks(content, content) 38 | return content 39 | } 40 | 41 | func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 42 | padding := blockSize - len(ciphertext)%blockSize 43 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 44 | return append(ciphertext, padtext...) 45 | } -------------------------------------------------------------------------------- /db/dbaccess/compression.go: -------------------------------------------------------------------------------- 1 | package dbaccess 2 | 3 | import ( 4 | "bytes" 5 | "compress/zlib" 6 | "io/ioutil" 7 | ) 8 | 9 | func Compress(s []byte) []byte { 10 | var b bytes.Buffer 11 | w := zlib.NewWriter(&b) 12 | w.Write(s) 13 | w.Close() 14 | return b.Bytes() 15 | } 16 | 17 | func Decompress(s []byte) ([]byte, error) { 18 | b := bytes.NewBuffer(s) 19 | r, err := zlib.NewReader(b) 20 | if err != nil { 21 | return []byte{}, err 22 | } 23 | result, err := ioutil.ReadAll(r) 24 | r.Close() 25 | if err != nil { 26 | return []byte{}, err 27 | } 28 | return result, nil 29 | } 30 | -------------------------------------------------------------------------------- /db/dbaccess/db.go: -------------------------------------------------------------------------------- 1 | package dbaccess 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | bolt "go.etcd.io/bbolt" 8 | 9 | "github.com/RunnersRevival/outrun/consts" 10 | ) 11 | 12 | var db *bolt.DB 13 | var battleDb *bolt.DB 14 | var DatabaseIsBusy = false 15 | 16 | func Set(bucket, key string, value []byte) error { 17 | CheckIfDBSet() 18 | value = Compress(value) // compress the input first 19 | err := db.Update(func(tx *bolt.Tx) error { 20 | bucket, err := tx.CreateBucketIfNotExists([]byte(bucket)) 21 | if err != nil { 22 | return err 23 | } 24 | err = bucket.Put([]byte(key), value) 25 | if err != nil { 26 | return err 27 | } 28 | return nil 29 | }) 30 | return err 31 | } 32 | 33 | func Get(bucket, key string) ([]byte, error) { 34 | CheckIfDBSet() 35 | var value []byte 36 | err := db.View(func(tx *bolt.Tx) error { 37 | b := tx.Bucket([]byte(bucket)) 38 | value = b.Get([]byte(key)) 39 | if value == nil { 40 | return errors.New("no value named '" + key + "' in bucket '" + bucket + "'") 41 | } 42 | return nil 43 | }) 44 | result, derr := Decompress(value) // decompress the result 45 | if derr != nil { 46 | return result, derr 47 | } 48 | return result, err 49 | } 50 | 51 | func Delete(bucket, key string) error { 52 | CheckIfDBSet() 53 | return db.Update(func(tx *bolt.Tx) error { 54 | return tx.Bucket([]byte(bucket)).Delete([]byte(key)) 55 | }) 56 | } 57 | 58 | func ForEachKey(bucket string, each func(k, v []byte) error) error { 59 | CheckIfDBSet() 60 | err := db.View(func(tx *bolt.Tx) error { 61 | b := tx.Bucket([]byte(bucket)) 62 | err2 := b.ForEach(each) 63 | return err2 64 | }) 65 | return err 66 | } 67 | 68 | func ForEachLogic(each func(tx *bolt.Tx) error) error { 69 | CheckIfDBSet() 70 | err := db.View(each) 71 | return err 72 | } 73 | 74 | func CheckIfDBSet() { 75 | if db == nil { 76 | bdb, err := bolt.Open(consts.DBFileName, 0600, &bolt.Options{Timeout: 3 * time.Second}) 77 | if err != nil { 78 | panic(err) 79 | } 80 | db = bdb 81 | } 82 | } 83 | 84 | // Battle stuff 85 | 86 | func CheckIfBattleDBSet() { 87 | if battleDb == nil { 88 | bdb, err := bolt.Open(consts.BattleDBFileName, 0600, &bolt.Options{Timeout: 3 * time.Second}) 89 | if err != nil { 90 | panic(err) 91 | } 92 | battleDb = bdb 93 | } 94 | } 95 | 96 | func BattleDBSet(bucket, key string, value []byte) error { 97 | CheckIfBattleDBSet() 98 | value = Compress(value) // compress the input first 99 | err := battleDb.Update(func(tx *bolt.Tx) error { 100 | bucket, err := tx.CreateBucketIfNotExists([]byte(bucket)) 101 | if err != nil { 102 | return err 103 | } 104 | err = bucket.Put([]byte(key), value) 105 | if err != nil { 106 | return err 107 | } 108 | return nil 109 | }) 110 | return err 111 | } 112 | 113 | func BattleDBGet(bucket, key string) ([]byte, error) { 114 | CheckIfBattleDBSet() 115 | var value []byte 116 | err := battleDb.View(func(tx *bolt.Tx) error { 117 | b := tx.Bucket([]byte(bucket)) 118 | value = b.Get([]byte(key)) 119 | if value == nil { 120 | return errors.New("no value named '" + key + "' in bucket '" + bucket + "'") 121 | } 122 | return nil 123 | }) 124 | result, derr := Decompress(value) // decompress the result 125 | if derr != nil { 126 | return result, derr 127 | } 128 | return result, err 129 | } 130 | 131 | func BattleDBDelete(bucket, key string) error { 132 | CheckIfBattleDBSet() 133 | return battleDb.Update(func(tx *bolt.Tx) error { 134 | return tx.Bucket([]byte(bucket)).Delete([]byte(key)) 135 | }) 136 | } 137 | 138 | func BattleDBForEachKey(bucket string, each func(k, v []byte) error) error { 139 | CheckIfBattleDBSet() 140 | err := battleDb.View(func(tx *bolt.Tx) error { 141 | b := tx.Bucket([]byte(bucket)) 142 | err2 := b.ForEach(each) 143 | return err2 144 | }) 145 | return err 146 | } 147 | 148 | func BattleDBForEachLogic(each func(tx *bolt.Tx) error) error { 149 | CheckIfBattleDBSet() 150 | err := battleDb.View(each) 151 | return err 152 | } 153 | -------------------------------------------------------------------------------- /emess/error_messages.go: -------------------------------------------------------------------------------- 1 | package emess 2 | 3 | const ( 4 | OK = "\u6210\u529f" 5 | BadPassword = "\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u4e0d\u6b63" 6 | ) 7 | -------------------------------------------------------------------------------- /enums/abilityType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | AbilityTypeNone = iota - 1 5 | AbilityTypeLaser 6 | AbilityTypeDrill 7 | AbilityTypeAsteroid 8 | AbilityTypeRingBonus 9 | AbilityTypeDistanceBonus 10 | AbilityTypeTrampoline 11 | AbilityTypeAnimal 12 | AbilityTypeCombo 13 | AbilityTypeMagnet 14 | AbilityTypeInvincible 15 | AbilityTypeNum 16 | AbilityType 17 | ) 18 | -------------------------------------------------------------------------------- /enums/advertEventType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | AdvertEventTypeRoulette = iota 5 | AdvertEventTypeCharacter 6 | AdvertEventTypeShop 7 | ) 8 | -------------------------------------------------------------------------------- /enums/boostItemType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | BoostItemUnknown = iota - 1 5 | BoostItemScoreBonus 6 | BoostItemAssistTrampoline 7 | BoostItemSubCharacter 8 | BoostItemNum 9 | ) 10 | -------------------------------------------------------------------------------- /enums/campaign.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | CampaignTypeBankedRingBonus = iota 5 | CampaignTypeDailyMissionBonus 6 | CampaignTypeChaoRouletteCost 7 | CampaignTypeGameItemCost 8 | CampaignTypeCharacterUpgradeCost 9 | CampaignTypePurchaseAddRings 10 | CampaignTypeJackpotValueBonus 11 | CampaignTypeMileagePassingRingBonus 12 | CampaignTypePurchaseAddEnergies 13 | CampaignTypePurchaseAddRedRings 14 | CampaignTypePurchaseAddRedRingsNoChargeUser 15 | CampaignTypeSendAddEnergies 16 | CampaignTypeInviteCount 17 | CampaignTypePremiumRouletteOdds 18 | CampaignTypeFreeWheelSpinCount 19 | CampaignTypeContinueCost 20 | CampaignTypePurchaseAddRaidEnergies 21 | ) 22 | -------------------------------------------------------------------------------- /enums/chaoDealing.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | ChaoDealingNone = iota 5 | ChaoDealingLeader 6 | ChaoDealingSub 7 | ) 8 | -------------------------------------------------------------------------------- /enums/chaoRarity.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | ChaoRarityNormal = iota 5 | ChaoRarityRare 6 | ChaoRaritySuperRare 7 | ChaoRarityNone 8 | ) 9 | -------------------------------------------------------------------------------- /enums/chaoStatus.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | ChaoStatusNotOwned = iota 5 | ChaoStatusOwned 6 | ChaoStatusMaxLevel 7 | ) 8 | -------------------------------------------------------------------------------- /enums/chaoType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | ChaoTypeMain = iota 5 | ChaoTypeSub 6 | ) 7 | -------------------------------------------------------------------------------- /enums/chaoWheelType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | ChaoWheelTypeNormal = int64(iota) 5 | ChaoWheelTypeSpecial 6 | ) 7 | -------------------------------------------------------------------------------- /enums/charaType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | // TODO: Apparently the IDs are different in game code versus what is sent. How is this achieved...? 4 | 5 | const ( 6 | CharaTypeUnknown = iota - 1 7 | CharaTypeSonic = iota + 300000 - 1 // used to match up with networked characters 8 | CharaTypeTails 9 | CharaTypeKnuckles 10 | CharaTypeAmy 11 | CharaTypeShadow 12 | CharaTypeBlaze 13 | CharaTypeRouge 14 | CharaTypeOmega 15 | CharaTypeBig 16 | CharaTypeCream 17 | CharaTypeEspio 18 | CharaTypeCharmy 19 | CharaTypeVector 20 | CharaTypeSilver 21 | CharaTypeMetalSonic 22 | CharaTypeClassicSonic 23 | CharaTypeWerehog 24 | CharaTypeSticks 25 | CharaTypeTikal 26 | CharaTypeMephiles 27 | CharaTypePSISilver 28 | CharaTypeMarine 29 | CharaTypeTangle 30 | CharaTypeWhisper 31 | CharaTypeAmitieAmy = iota + 301000 - 25 // event characters start with 301 32 | CharaTypeGothicAmy 33 | CharaTypeHalloweenShadow 34 | CharaTypeHalloweenRouge 35 | CharaTypeHalloweenOmega 36 | CharaTypeXMasSonic 37 | CharaTypeXMasTails 38 | CharaTypeXMasKnuckles 39 | CharaTypeXT 40 | ) 41 | 42 | const ( 43 | CTStrSonic = "300000" 44 | CTStrTails = "300001" 45 | CTStrKnuckles = "300002" 46 | CTStrAmy = "300003" 47 | CTStrShadow = "300004" 48 | CTStrBlaze = "300005" 49 | CTStrRouge = "300006" 50 | CTStrOmega = "300007" 51 | CTStrBig = "300008" 52 | CTStrCream = "300009" 53 | CTStrEspio = "300010" 54 | CTStrCharmy = "300011" 55 | CTStrVector = "300012" 56 | CTStrSilver = "300013" 57 | CTStrMetalSonic = "300014" 58 | CTStrClassicSonic = "300015" 59 | CTStrWerehog = "300016" 60 | CTStrSticks = "300017" 61 | CTStrTikal = "300018" 62 | CTStrMephiles = "300019" 63 | CTStrPSISilver = "300020" 64 | CTStrMarine = "300021" 65 | CTStrTangle = "300022" 66 | CTStrWhisper = "300023" 67 | CTStrAmitieAmy = "301000" 68 | CTStrGothicAmy = "301001" 69 | CTStrHalloweenShadow = "301002" 70 | CTStrHalloweenRouge = "301003" 71 | CTStrHalloweenOmega = "301004" 72 | CTStrXMasSonic = "301005" 73 | CTStrXMasTails = "301006" 74 | CTStrXMasKnuckles = "301007" 75 | CTStrXT = "301008" 76 | ) 77 | -------------------------------------------------------------------------------- /enums/characterStatus.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | CharacterStatusLocked = iota 5 | CharacterStatusUnlocked 6 | CharacterStatusMaxLevel 7 | ) 8 | -------------------------------------------------------------------------------- /enums/collectEventType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | CollectEventTypeGetAnimals = iota 5 | CollectEventTypeGetRing 6 | CollectEventTypeRunDistance 7 | ) 8 | -------------------------------------------------------------------------------- /enums/dailyMissions.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | // TODO: Remove me! I am no longer being used! 4 | const ( 5 | DailyMissionDontKnowYet = 68 6 | ) 7 | -------------------------------------------------------------------------------- /enums/eventIDs.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | EventIDSpecialStage = 100000000 * (iota + 1) 5 | EventIDRaidBoss 6 | EventIDCollectObject 7 | EventIDGacha 8 | EventIDAdvert 9 | EventIDQuick 10 | EventIDBGM 11 | ) 12 | -------------------------------------------------------------------------------- /enums/eventTypes.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | EventTypeSpecialStage = iota 5 | EventTypeRaidBoss 6 | EventTypeCollectObject 7 | EventTypeGacha 8 | EventTypeAdvert 9 | EventTypeQuick 10 | EventTypeBGM 11 | ) 12 | -------------------------------------------------------------------------------- /enums/idTypes.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | // Game divides these numbers by 10000 in order to determine 4 | // the kind of item that the server sends its way. 5 | 6 | const ( 7 | IDTypeNone = -1 8 | ) 9 | const ( 10 | IDTypeBoostItem = iota + 110000 11 | IDTypeEquipItem 12 | ) 13 | const ( 14 | IDTypeItemRouletteWin = iota + 200000 15 | IDTypeRouletteToken 16 | IDTypeEggItem 17 | IDTypePremiumRouletteTicket 18 | IDTypeItemRouletteTicket 19 | ) 20 | const ( 21 | IDTypeChara = 300000 22 | IDTypeChao = 400000 23 | ) 24 | const ( 25 | IDTypeRedRing = iota + 900000 26 | IDTypeRing 27 | IDTypeEnergy 28 | IDTypeEnergyMax 29 | ) 30 | const ( 31 | IDTypeRaidRing = 960000 32 | ) 33 | -------------------------------------------------------------------------------- /enums/ids.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | IDBig = iota + 200000 5 | IDSuper 6 | IDJackpot 7 | ) 8 | const ( 9 | IDRouletteToken = 210000 10 | IDSpecialEgg = 220000 11 | IDRouletteTicketBegin = 229999 12 | IDRouletteTicketPremium = 230000 13 | IDRouletteTicketItem = 240000 14 | IDRouletteTicketRaid = 250000 15 | IDRouletteTicketEvent = 260000 16 | IDRouletteTicketEnd = 299999 17 | IDCharaBegin = 300000 18 | IDChaoBegin = 400000 19 | IDChaoBeginRare = 401000 20 | IDChaoBeginSuperRare = 402000 21 | IDRedRing = 900000 // RSRing = "Red Star Ring" 22 | IDRedRing0 = 900010 23 | IDRedRing1 = 900030 24 | IDRedRing2 = 900060 25 | IDRedRing3 = 900210 26 | IDRedRing4 = 900380 27 | IDRing = 910000 28 | IDRing0 = 910021 29 | IDRing1 = 910045 30 | IDRing2 = 910094 31 | IDRing3 = 910147 32 | IDRing4 = 910204 33 | IDRing5 = 910265 34 | IDEnergy = 920000 35 | IDEnergy0 = 920001 36 | IDEnergy1 = 920005 37 | IDEnergy2 = 920010 38 | IDEnergy3 = 920015 39 | IDEnergy4 = 920020 40 | IDEnergy5 = 930005 41 | IDEnergyMax = 930000 42 | IDSubChara = 940000 43 | IDContinue = 950000 44 | IDRaidRing = 960000 45 | IDDailyBattleReset0 = 980000 46 | IDDailyBattleReset1 = 980001 47 | IDDailyBattleReset2 = 980002 48 | ) 49 | -------------------------------------------------------------------------------- /enums/incentiveTypes.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | IncentiveTypeNone = iota 5 | IncentiveTypePoint 6 | IncentiveTypeChapter 7 | IncentiveTypeEpisode // Most likely the one that we need 8 | IncentiveTypeFriend 9 | ) 10 | -------------------------------------------------------------------------------- /enums/itemIDs.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | import "strconv" 4 | 5 | const ( 6 | ItemIDNone = -1 7 | ) 8 | const ( 9 | ItemIDBoostScore = int64(iota) + 110000 10 | ItemIDBoostTrampoline 11 | ItemIDBoostSubChara 12 | ) 13 | const ( 14 | ItemIDInvincible = int64(iota) + 120000 15 | ItemIDBarrier 16 | ItemIDMagnet 17 | ItemIDTrampoline 18 | ItemIDCombo 19 | ItemIDLaser 20 | ItemIDDrill 21 | ItemIDAsteroid 22 | ItemIDRingBonus 23 | ItemIDDistanceBonus 24 | ItemIDAnimalBonus 25 | ) 26 | 27 | var ( 28 | ItemIDStrBoostScore = strconv.Itoa(int(ItemIDBoostScore)) 29 | ItemIDStrBoostTrampoline = strconv.Itoa(int(ItemIDBoostTrampoline)) 30 | ItemIDStrBoostSubChara = strconv.Itoa(int(ItemIDBoostSubChara)) 31 | ) 32 | 33 | var ( 34 | ItemIDStrInvincible = strconv.Itoa(int(ItemIDInvincible)) 35 | ItemIDStrBarrier = strconv.Itoa(int(ItemIDBarrier)) 36 | ItemIDStrMagnet = strconv.Itoa(int(ItemIDMagnet)) 37 | ItemIDStrTrampoline = strconv.Itoa(int(ItemIDTrampoline)) 38 | ItemIDStrCombo = strconv.Itoa(int(ItemIDCombo)) 39 | ItemIDStrLaser = strconv.Itoa(int(ItemIDLaser)) 40 | ItemIDStrDrill = strconv.Itoa(int(ItemIDDrill)) 41 | ItemIDStrAsteroid = strconv.Itoa(int(ItemIDAsteroid)) 42 | ItemIDStrRingBonus = strconv.Itoa(int(ItemIDRingBonus)) 43 | ItemIDStrDistanceBonus = strconv.Itoa(int(ItemIDDistanceBonus)) 44 | ItemIDStrAnimalBonus = strconv.Itoa(int(ItemIDAnimalBonus)) 45 | ) 46 | 47 | const ( 48 | ItemIDPackedInvincible0 = int64(iota) + 120100 49 | ItemIDPackedBarrier0 50 | ItemIDPackedMagnet0 51 | ItemIDPackedTrampoline0 52 | ItemIDPackedCombo0 53 | ItemIDPackedLaser0 54 | ItemIDPackedDrill0 55 | ItemIDPackedAsteroid0 56 | ItemIDPackedRingBonus0 57 | ItemIDPackedDistanceBonus0 58 | ItemIDPackedAnimalBonus0 59 | ) 60 | const ( 61 | ItemIDPackedInvincible1 = int64(iota) + 121000 62 | ItemIDPackedBarrier1 63 | ItemIDPackedMagnet1 64 | ItemIDPackedTrampoline1 65 | ItemIDPackedCombo1 66 | ItemIDPackedLaser1 67 | ItemIDPackedDrill1 68 | ItemIDPackedAsteroid1 69 | ItemIDPackedRingBonus1 70 | ItemIDPackedDistanceBonus1 71 | ItemIDPackedAnimalBonus1 72 | ) 73 | 74 | const ( // Mileage rewards 75 | ItemIDRedRing = 900000 76 | ItemIDRedRing0 = 900010 77 | ItemIDRedRing1 = 900030 78 | ItemIDRedRing2 = 900060 79 | ItemIDRedRing3 = 900210 80 | ItemIDRedRing4 = 900380 81 | ItemIDRing = 910000 82 | ItemIDRing0 = 910021 83 | ItemIDRing1 = 910045 84 | ItemIDRing2 = 910094 85 | ItemIDRing3 = 910147 86 | ItemIDRing4 = 910204 87 | ItemIDRing5 = 910265 88 | ) 89 | 90 | const ( 91 | ItemIDEnergy = 920000 92 | ItemIDEnergy0 = 920005 93 | ItemIDEnergy1 = 920020 94 | ItemIDEnergy2 = 920030 95 | ItemIDEnergy3 = 920050 96 | ItemIDEnergy4 = 920100 97 | ) 98 | 99 | const ( 100 | ItemIDRaidbossEnergy = 940000 101 | ItemIDRaidbossRing = 960000 102 | ) 103 | 104 | const ( 105 | ItemIDStrRedRing = "900000" 106 | ItemIDStrRing = "910000" 107 | ) 108 | 109 | const ( 110 | ItemIDStrItemRouletteTicket = "240000" 111 | ItemIDStrPremiumRouletteTicket = "230000" 112 | ) 113 | -------------------------------------------------------------------------------- /enums/itemType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | ItemTypeTimerGold = iota - 5 5 | ItemTypeTimerSilver 6 | ItemTypeTimerBronze 7 | ItemTypeRedRing 8 | ItemTypeUnknown 9 | ItemTypeBegin 10 | ) 11 | const ( 12 | ItemTypeInvincible = iota 13 | ItemTypeBarrier 14 | ItemTypeMagnet 15 | ItemTypeTrampoline 16 | ItemTypeCombo 17 | ItemTypeLaser 18 | ItemTypeDrill 19 | ItemTypeAsteroid 20 | ItemTypeNum 21 | ) 22 | const ( 23 | ItemTypePhantomStart = 5 24 | ItemTypePhantomEnd = 8 25 | ) 26 | -------------------------------------------------------------------------------- /enums/language.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | LangJapanese = iota 5 | LangEnglish 6 | LangChineseZHJ 7 | LangChineseZH 8 | LangKorean 9 | LangFrench 10 | LangGerman 11 | LangSpanish 12 | LangPortuguese 13 | LangItalian 14 | LangRussian 15 | ) 16 | -------------------------------------------------------------------------------- /enums/lockCondition.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | LockConditionOpen = iota 5 | LockConditionMileageEpisode 6 | LockConditionRingOrRedRing 7 | LockConditionRoulette 8 | ) 9 | -------------------------------------------------------------------------------- /enums/rankingLeague.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | RankingLeagueNone = iota - 1 5 | RankingLeagueF_M 6 | RankingLeagueF 7 | RankingLeagueF_P 8 | RankingLeagueE_M 9 | RankingLeagueE 10 | RankingLeagueE_P 11 | RankingLeagueD_M 12 | RankingLeagueD 13 | RankingLeagueD_P 14 | RankingLeagueC_M 15 | RankingLeagueC 16 | RankingLeagueC_P 17 | RankingLeagueB_M 18 | RankingLeagueB 19 | RankingLeagueB_P 20 | RankingLeagueA_M 21 | RankingLeagueA 22 | RankingLeagueA_P 23 | RankingLeagueS_M 24 | RankingLeagueS 25 | RankingLeagueS_P 26 | ) 27 | -------------------------------------------------------------------------------- /enums/rankingMode.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | RankingModeEndless = iota 5 | RankingModeQuick 6 | ) 7 | -------------------------------------------------------------------------------- /enums/rewardType.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | RewardTypeNone = iota - 1 5 | RewardTypeItemInvincible 6 | RewardTypeItemBarrier 7 | RewardTypeItemMagnet 8 | RewardTypeItemTrampoline 9 | RewardTypeItemCombo 10 | RewardTypeItemLaser 11 | RewardTypeItemDrill 12 | RewardTypeItemAsteroid 13 | RewardTypeRing 14 | RewardTypeRedRing 15 | RewardTypeMissionIncentiveCount 16 | ) 17 | const ( 18 | RewardTypeItemRoulettePrizeCount = 10 19 | RewardTypeCharaBegin = 300000 20 | RewardTypeChaoBegin = 400000 21 | RewardTypeEnergy = 920000 22 | RewardTypeSpecialEgg = 220000 23 | ) 24 | -------------------------------------------------------------------------------- /enums/upgradeAbilities.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | UpgradeAbilityInvincble = iota + 120000 5 | UpgradeAbilityMagnet = iota + 120001 6 | UpgradeAbilityTrampoline 7 | UpgradeAbilityCombo 8 | UpgradeAbilityLaser 9 | UpgradeAbilityDrill 10 | UpgradeAbilityAsteroid 11 | UpgradeAbilityRingBonus 12 | UpgradeAbilityDistanceBonus 13 | UpgradeAbilityAnimalBonus 14 | ) 15 | -------------------------------------------------------------------------------- /enums/wheelRank.go: -------------------------------------------------------------------------------- 1 | package enums 2 | 3 | const ( 4 | WheelRankNormal = iota 5 | WheelRankBig 6 | WheelRankSuper 7 | WheelRankMax 8 | ) 9 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/RunnersRevival/outrun 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/gorilla/mux v1.8.0 7 | github.com/jinzhu/now v1.1.4 8 | github.com/shirou/gopsutil v3.21.11+incompatible 9 | go.etcd.io/bbolt v1.3.6 10 | ) 11 | 12 | require ( 13 | github.com/go-ole/go-ole v1.2.6 // indirect 14 | github.com/stretchr/testify v1.7.0 // indirect 15 | github.com/tklauser/go-sysconf v0.3.9 // indirect 16 | github.com/tklauser/numcpus v0.3.0 // indirect 17 | github.com/yusufpapurcu/wmi v1.2.2 // indirect 18 | golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 4 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 5 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 6 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 7 | github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= 8 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 9 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 11 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= 12 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 15 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= 17 | github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= 18 | github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= 19 | github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= 20 | github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= 21 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 22 | go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= 23 | go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= 24 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 25 | golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 26 | golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s= 27 | golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 29 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | -------------------------------------------------------------------------------- /helper/responses.go: -------------------------------------------------------------------------------- 1 | package helper 2 | -------------------------------------------------------------------------------- /inforeporters/stats.go: -------------------------------------------------------------------------------- 1 | package inforeporters 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | "runtime" 8 | "time" 9 | 10 | "github.com/shirou/gopsutil/cpu" 11 | ) 12 | 13 | type statsInfo struct { 14 | AllocatedMem uint64 `json:"allocatedMemory"` 15 | GoroutineCount int `json:"goroutineCount"` 16 | CPUUsages []float64 `json:"cpuUsages"` 17 | } 18 | 19 | func Stats(w http.ResponseWriter, r *http.Request) { 20 | var m runtime.MemStats 21 | runtime.ReadMemStats(&m) 22 | allocated := m.Alloc / 1024 / 1024 // megabytes 23 | goroutines := runtime.NumGoroutine() 24 | cpus, err := cpu.Percent(10*time.Millisecond, false) 25 | if err != nil { 26 | // ignore for now 27 | cpus = []float64{0.0} 28 | } 29 | stats := statsInfo{ 30 | allocated, 31 | goroutines, 32 | cpus, 33 | } 34 | jout, err := json.Marshal(stats) 35 | if err != nil { 36 | log.Println("error collecting stats: " + err.Error()) 37 | } 38 | w.Write(jout) 39 | } 40 | -------------------------------------------------------------------------------- /log/error.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | // TODO: Abandon this file. All functions here are applied within the Helper struct. 9 | const ( 10 | PREFIX_ERR = "ERR" 11 | PREFIX_OUT = "OUT" 12 | PREFIX_WARN = "WARN" 13 | PREFIX_UNCATCHABLE_ERR = "UNCATCHABLE ERR" 14 | 15 | LOGOUT_BASE = "{%s} (%s) %s\n" 16 | LOGERR_BASE = "{%s} (%s) %s: %s\n" 17 | 18 | INTERNAL_SRV_ERR = "Internal server error" 19 | BAD_REQUEST = "Bad request" 20 | ) 21 | 22 | type Reporter struct { 23 | CallerName string 24 | RespW http.ResponseWriter 25 | } 26 | 27 | func MakeReporter(callerName string, r http.ResponseWriter) *Reporter { 28 | return &Reporter{ 29 | callerName, 30 | r, 31 | } 32 | } 33 | 34 | func (r *Reporter) Out(msg string) { 35 | log.Printf(LOGOUT_BASE, PREFIX_OUT, r.CallerName, msg) 36 | } 37 | func (r *Reporter) Warn(msg string) { 38 | log.Printf(LOGOUT_BASE, PREFIX_WARN, r.CallerName, msg) 39 | } 40 | func (r *Reporter) Uncatchable(msg string) { 41 | log.Printf(LOGOUT_BASE, PREFIX_OUT, r.CallerName, msg) 42 | } 43 | func (r *Reporter) InternalErr(msg string, err error) { 44 | log.Printf(LOGERR_BASE, PREFIX_ERR, r.CallerName, msg, err.Error()) 45 | r.RespW.WriteHeader(http.StatusBadRequest) 46 | r.RespW.Write([]byte(BAD_REQUEST)) 47 | } 48 | func (r *Reporter) Err(msg string, err error) { 49 | log.Printf(LOGERR_BASE, PREFIX_ERR, r.CallerName, msg, err.Error()) 50 | r.RespW.WriteHeader(http.StatusBadRequest) 51 | r.RespW.Write([]byte(BAD_REQUEST)) 52 | } 53 | func (r *Reporter) InternalFatal(msg string, err error) { 54 | log.Fatalf(LOGERR_BASE, PREFIX_ERR, r.CallerName, msg, err.Error()) 55 | r.RespW.WriteHeader(http.StatusBadRequest) 56 | r.RespW.Write([]byte(BAD_REQUEST)) 57 | } 58 | func (r *Reporter) Fatal(msg string, err error) { 59 | log.Fatalf(LOGERR_BASE, PREFIX_ERR, r.CallerName, msg, err.Error()) 60 | r.RespW.WriteHeader(http.StatusBadRequest) 61 | r.RespW.Write([]byte(BAD_REQUEST)) 62 | } 63 | -------------------------------------------------------------------------------- /logic/battle/drawBattleRival.go: -------------------------------------------------------------------------------- 1 | package battle 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/RunnersRevival/outrun/consts" 9 | "github.com/RunnersRevival/outrun/db" 10 | "github.com/RunnersRevival/outrun/db/dbaccess" 11 | "github.com/RunnersRevival/outrun/netobj" 12 | ) 13 | 14 | // TODO: Add stricter algorithm for the more expensive "Find a Beatable Opponent" reroll option 15 | func DrawBattleRival(player netobj.Player, limit int) netobj.BattleState { 16 | if !player.BattleState.MatchedUpWithRival { // are we not matched up yet? 17 | db.BattleSaveWaitingPlayer(player) // Save player in the waiting pool 18 | playerIDs := []string{} 19 | dbaccess.BattleDBForEachKey(consts.BattleDBBucketWaiting, func(k, v []byte) error { 20 | playerIDs = append(playerIDs, string(k)) 21 | return nil 22 | }) 23 | currentTime := time.Now().UTC().Unix() 24 | rivalID := "" 25 | iterations := 0 26 | for len(playerIDs) > 0 { 27 | index := rand.Intn(len(playerIDs)) 28 | potentialRival, err := db.GetPlayer(playerIDs[index]) 29 | if err != nil { 30 | log.Printf("[WARN] (battle.DrawBattleRival) Unable to get player '%s': %s", playerIDs[index], err.Error()) 31 | } else { 32 | if player.ID != playerIDs[index] && 33 | potentialRival.BattleState.ScoreRecordedToday && 34 | !potentialRival.BattleState.MatchedUpWithRival && 35 | currentTime < potentialRival.BattleState.BattleEndsAt && 36 | (potentialRival.BattleState.PrevDailyBattleHighScore < player.BattleState.PrevDailyBattleHighScore - 1500000) && 37 | potentialRival.BattleState.PrevDailyBattleHighScore > player.BattleState.PrevDailyBattleHighScore + 1500000{ 38 | rivalID = playerIDs[index] 39 | break 40 | } else { 41 | if player.ID != playerIDs[index] && // Player is none of the above, default to fully randomized rival 42 | potentialRival.BattleState.ScoreRecordedToday && 43 | !potentialRival.BattleState.MatchedUpWithRival && 44 | currentTime < potentialRival.BattleState.BattleEndsAt { 45 | rivalID = playerIDs[index] 46 | break 47 | } 48 | } 49 | } 50 | playerIDs[index] = playerIDs[len(playerIDs)-1] 51 | playerIDs = playerIDs[:len(playerIDs)-1] 52 | iterations++ 53 | if iterations >= limit && limit > 0 { 54 | break 55 | } 56 | } 57 | if len(rivalID) > 0 { 58 | rival, err := db.GetPlayer(rivalID) 59 | if err != nil { 60 | log.Printf("[WARN] (battle.DrawBattleRival) Unable to get player '%s': %s", rivalID, err.Error()) 61 | } else { 62 | rival.BattleState.RivalID = player.ID 63 | rival.BattleState.MatchedUpWithRival = true 64 | db.BattleDeleteWaitingPlayer(player.ID) 65 | db.BattleDeleteWaitingPlayer(rival.ID) 66 | db.BattleSaveMatchedPlayer(player) 67 | db.BattleSaveMatchedPlayer(rival) 68 | err = db.SavePlayer(rival) 69 | if err != nil { 70 | log.Printf("[WARN] (battle.DrawBattleRival) Unable to save rival data: %s", err.Error()) 71 | } else { 72 | player.BattleState.RivalID = rivalID 73 | player.BattleState.MatchedUpWithRival = true 74 | db.BattleDeleteWaitingPlayer(rival.ID) 75 | db.BattleDeleteWaitingPlayer(rival.ID) 76 | db.BattleSaveMatchedPlayer(player) 77 | db.BattleSaveMatchedPlayer(rival) 78 | } 79 | } 80 | } 81 | } else { 82 | db.BattleSaveMatchedPlayer(player) 83 | db.BattleDeleteWaitingPlayer(player.ID) // Player is matched, delete entry from the waiting pool 84 | } 85 | if player.ID == player.BattleState.RivalID && player.BattleState.MatchedUpWithRival { 86 | log.Printf("[WARN] (battle.DrawBattleRival) Somehow matched up with self! Removing match...") 87 | player.BattleState.MatchedUpWithRival = false 88 | db.BattleDeleteMatchedPlayer(player.ID) 89 | db.BattleSaveWaitingPlayer(player) 90 | } 91 | return player.BattleState 92 | } 93 | -------------------------------------------------------------------------------- /logic/conversion/configuredCampaignToCampaign.go: -------------------------------------------------------------------------------- 1 | package conversion 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/config/campaignconf" 7 | "github.com/RunnersRevival/outrun/obj" 8 | "github.com/jinzhu/now" 9 | ) 10 | 11 | func ConfiguredCampaignToCampaign(cc campaignconf.ConfiguredCampaign) obj.Campaign { 12 | // Should be used by the game as soon as possible 13 | startTime := cc.StartTime 14 | switch startTime { 15 | case -2: 16 | startTime = now.BeginningOfDay().UTC().Unix() 17 | case -3: 18 | startTime = now.EndOfDay().UTC().Unix() 19 | case -4: 20 | startTime = time.Now().UTC().Unix() - 1 21 | } 22 | endTime := cc.EndTime 23 | switch endTime { 24 | case -2: 25 | endTime = now.BeginningOfDay().UTC().Unix() 26 | case -3: 27 | endTime = now.EndOfDay().UTC().Unix() 28 | case -4: 29 | endTime = time.Now().UTC().Add(24 * time.Hour).Unix() 30 | } 31 | newEvent := obj.Campaign{ 32 | cc.RealType(), 33 | cc.Content, 34 | cc.SubContent, 35 | startTime, 36 | endTime, 37 | } 38 | return newEvent 39 | } 40 | -------------------------------------------------------------------------------- /logic/conversion/configuredEventToEvent.go: -------------------------------------------------------------------------------- 1 | package conversion 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/config/eventconf" 7 | "github.com/RunnersRevival/outrun/obj" 8 | "github.com/jinzhu/now" 9 | ) 10 | 11 | func ConfiguredEventToEvent(ce eventconf.ConfiguredEvent) obj.Event { 12 | // Should be used by the game as soon as possible 13 | startTime := ce.StartTime 14 | switch startTime { 15 | case -2: 16 | startTime = now.BeginningOfDay().Unix() 17 | case -3: 18 | startTime = now.EndOfDay().Unix() 19 | case -4: 20 | startTime = time.Now().Unix() - 1 21 | } 22 | endTime := ce.EndTime 23 | switch endTime { 24 | case -2: 25 | endTime = now.BeginningOfDay().Unix() 26 | case -3: 27 | endTime = now.EndOfDay().Unix() 28 | case -4: 29 | endTime = time.Now().Add(24 * time.Hour).Unix() 30 | } 31 | newEvent := obj.Event{ 32 | // Ex. ID: 712340000 33 | (ce.ID * 10000) + ce.RealType(), // make a real event ID 34 | ce.RealType(), 35 | startTime, 36 | endTime, 37 | endTime, 38 | } 39 | return newEvent 40 | } 41 | -------------------------------------------------------------------------------- /logic/conversion/configuredInfoToInformation.go: -------------------------------------------------------------------------------- 1 | package conversion 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/config/infoconf" 7 | "github.com/RunnersRevival/outrun/obj" 8 | "github.com/jinzhu/now" 9 | ) 10 | 11 | func ConfiguredInfoToInformation(ci infoconf.ConfiguredInfo) obj.Information { 12 | // Should be used by the game as soon as possible 13 | startTime := ci.StartTime 14 | switch startTime { 15 | case -2: 16 | startTime = now.BeginningOfDay().Unix() 17 | case -3: 18 | startTime = now.EndOfDay().Unix() 19 | case -4: 20 | startTime = time.Now().Unix() - 1 21 | } 22 | endTime := ci.EndTime 23 | switch endTime { 24 | case -2: 25 | endTime = now.BeginningOfDay().Unix() 26 | case -3: 27 | endTime = now.EndOfDay().Unix() 28 | case -4: 29 | endTime = time.Now().Add(24 * time.Hour).Unix() 30 | } 31 | newInfo := obj.Information{ 32 | ci.ID, 33 | ci.Priority, 34 | startTime, 35 | endTime, 36 | ci.ConstructParam(), 37 | } 38 | return newInfo 39 | } 40 | -------------------------------------------------------------------------------- /logic/conversion/configuredTickerToTicker.go: -------------------------------------------------------------------------------- 1 | package conversion 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/config/infoconf" 7 | "github.com/RunnersRevival/outrun/obj" 8 | "github.com/jinzhu/now" 9 | ) 10 | 11 | func ConfiguredTickerToTicker(index int64, ct infoconf.ConfiguredTicker) obj.Ticker { 12 | // Should be used by the game as soon as possible 13 | startTime := ct.StartTime 14 | switch startTime { 15 | case -2: 16 | startTime = now.BeginningOfDay().Unix() 17 | case -3: 18 | startTime = now.EndOfDay().Unix() 19 | case -4: 20 | startTime = time.Now().Unix() - 1 21 | } 22 | endTime := ct.EndTime 23 | switch endTime { 24 | case -2: 25 | endTime = now.BeginningOfDay().Unix() 26 | case -3: 27 | endTime = now.EndOfDay().Unix() 28 | case -4: 29 | endTime = time.Now().Add(24 * time.Hour).Unix() 30 | } 31 | newTicker := obj.Ticker{ 32 | index, 33 | startTime, 34 | endTime, 35 | ct.Message, 36 | } 37 | return newTicker 38 | } 39 | -------------------------------------------------------------------------------- /logic/conversion/playerToBattleData.go: -------------------------------------------------------------------------------- 1 | package conversion 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/enums" 5 | "github.com/RunnersRevival/outrun/netobj" 6 | "github.com/RunnersRevival/outrun/obj" 7 | ) 8 | 9 | func DebugPlayerToBattleData(player netobj.Player) obj.BattleData { 10 | uid := player.ID 11 | username := player.Username 12 | maxScore := player.BattleState.DailyBattleHighScore // TODO: Should this be the daily high score? 13 | league := player.PlayerState.RankingLeague 14 | loginTime := player.LastLogin 15 | mainChaoID := player.PlayerState.MainChaoID 16 | mainChaoLevel := player.PlayerState.MainChaoLevel // TODO: this may be problematic if the game does checks 17 | subChaoID := player.PlayerState.SubChaoID 18 | subChaoLevel := player.PlayerState.SubChaoLevel // TODO: this may be problematic if the game does checks 19 | rank := player.PlayerState.Rank 20 | mainCharaID := player.PlayerState.MainCharaID 21 | mainCharaLevel := player.PlayerState.MainCharaLevel // TODO: this may be problematic if the game does checks 22 | subCharaID := player.PlayerState.SubCharaID 23 | subCharaLevel := int64(0) // TODO: this may be problematic if the game does checks 24 | goOnWin := player.BattleState.WinStreak 25 | isSentEnergy := int64(0) 26 | language := int64(enums.LangEnglish) 27 | return obj.BattleData{ 28 | uid, 29 | username, 30 | maxScore, 31 | league, 32 | loginTime, 33 | mainChaoID, 34 | mainChaoLevel, 35 | subChaoID, 36 | subChaoLevel, 37 | rank, 38 | mainCharaID, 39 | mainCharaLevel, 40 | subCharaID, 41 | subCharaLevel, 42 | goOnWin, 43 | isSentEnergy, 44 | language, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /logic/conversion/playerToLeaderboardEntry.go: -------------------------------------------------------------------------------- 1 | package conversion 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/enums" 5 | "github.com/RunnersRevival/outrun/netobj" 6 | "github.com/RunnersRevival/outrun/obj" 7 | "github.com/jinzhu/now" 8 | ) 9 | 10 | func PlayerToLeaderboardEntry(player netobj.Player, mode, lbtype int64) obj.LeaderboardEntry { 11 | friendID := player.ID 12 | name := player.Username 13 | url := player.Username + "_findme" // TODO: only used for testing right now 14 | grade := int64(1) // ONLY FOR TESTING 15 | exposeOnline := int64(0) 16 | rankingScore := player.PlayerState.HighScore 17 | if lbtype == 8 || lbtype == 9 { 18 | // special stage ranking 19 | rankingScore = player.EventState.Param 20 | } 21 | rankChanged := int64(0) 22 | isSentEnergy := int64(0) 23 | expireTime := now.EndOfWeek().UTC().Unix() 24 | numRank := player.PlayerState.Rank 25 | loginTime := player.LastLogin 26 | mainCharaID := player.PlayerState.MainCharaID 27 | mainCharaLevel := int64(0) 28 | subCharaID := player.PlayerState.SubCharaID 29 | subCharaLevel := int64(0) 30 | mainChaoID := player.PlayerState.MainChaoID 31 | mainChaoLevel := int64(0) 32 | subChaoID := player.PlayerState.SubChaoID 33 | subChaoLevel := int64(0) 34 | if player.IndexOfChara(mainCharaID) != -1 { 35 | mainCharaLevel = player.CharacterState[player.IndexOfChara(mainCharaID)].Level 36 | } 37 | if player.IndexOfChara(subCharaID) != -1 { 38 | subCharaLevel = player.CharacterState[player.IndexOfChara(subCharaID)].Level 39 | } 40 | if player.IndexOfChao(mainChaoID) != -1 { 41 | mainChaoLevel = player.ChaoState[player.IndexOfChao(mainChaoID)].Level 42 | } 43 | if player.IndexOfChao(subChaoID) != -1 { 44 | subChaoLevel = player.ChaoState[player.IndexOfChao(subChaoID)].Level 45 | } 46 | language := int64(enums.LangEnglish) 47 | league := player.PlayerState.RankingLeague 48 | maxScore := player.PlayerState.HighScore 49 | if mode == 1 { //timed mode? 50 | rankingScore = player.PlayerState.TimedHighScore 51 | league = player.PlayerState.QuickRankingLeague 52 | maxScore = player.PlayerState.TimedHighScore 53 | } 54 | return obj.LeaderboardEntry{ 55 | friendID, 56 | name, 57 | url, 58 | grade, 59 | exposeOnline, 60 | rankingScore, 61 | rankChanged, 62 | isSentEnergy, 63 | expireTime, 64 | numRank, 65 | loginTime, 66 | mainCharaID, 67 | mainCharaLevel, 68 | subCharaID, 69 | subCharaLevel, 70 | mainChaoID, 71 | mainChaoLevel, 72 | subChaoID, 73 | subChaoLevel, 74 | language, 75 | league, 76 | maxScore, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /logic/finders.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/RunnersRevival/outrun/consts" 7 | "github.com/RunnersRevival/outrun/db" 8 | "github.com/RunnersRevival/outrun/db/dbaccess" 9 | "github.com/RunnersRevival/outrun/netobj" 10 | ) 11 | 12 | func FindPlayersByPassword(password string, silent bool) ([]netobj.Player, error) { 13 | playerIDs := []string{} 14 | players := []netobj.Player{} 15 | dbaccess.ForEachKey(consts.DBBucketPlayers, func(k, v []byte) error { 16 | playerIDs = append(playerIDs, string(k)) 17 | return nil 18 | }) 19 | for _, pid := range playerIDs { 20 | player, err := db.GetPlayer(pid) 21 | if err != nil { 22 | if silent { 23 | log.Printf("[WARN] (logic.FindPlayersByPassword) Unable to get player '%s': %s", pid, err.Error()) 24 | } else { 25 | return []netobj.Player{}, err 26 | } 27 | } 28 | if player.Password == password { 29 | players = append(players, player) 30 | } 31 | } 32 | return players, nil 33 | } 34 | 35 | func FindPlayersByMigrationPassword(password string, silent bool) ([]netobj.Player, error) { 36 | playerIDs := []string{} 37 | players := []netobj.Player{} 38 | dbaccess.ForEachKey(consts.DBBucketPlayers, func(k, v []byte) error { 39 | playerIDs = append(playerIDs, string(k)) 40 | return nil 41 | }) 42 | for _, pid := range playerIDs { 43 | player, err := db.GetPlayer(pid) 44 | if err != nil { 45 | if silent { 46 | log.Printf("[WARN] (logic.FindPlayersByMigrationPassword) Unable to get player '%s': %s", pid, err.Error()) 47 | } else { 48 | return []netobj.Player{}, err 49 | } 50 | } 51 | if player.MigrationPassword == password { 52 | players = append(players, player) 53 | } 54 | } 55 | return players, nil 56 | } 57 | -------------------------------------------------------------------------------- /logic/gameplay/requiredItemPayment.go: -------------------------------------------------------------------------------- 1 | package gameplay 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/obj" 6 | "github.com/RunnersRevival/outrun/obj/constobjs" 7 | ) 8 | 9 | func findItem(id string) obj.ConsumedItem { 10 | var result obj.ConsumedItem 11 | for _, citem := range constobjs.DefaultCostList { 12 | if citem.ID == id { 13 | result = citem 14 | break 15 | } 16 | } 17 | return result 18 | } 19 | 20 | func GetRequiredItemPayment(items []string, player netobj.Player) int64 { 21 | totalRingPayment := int64(0) 22 | for _, itemID := range items { 23 | citem := findItem(itemID) 24 | if itemID[:2] == "11" { // boosts, not items 25 | totalRingPayment += citem.Item.Amount 26 | } else { 27 | index := player.IndexOfItem(itemID) 28 | if player.PlayerState.Items[index].Amount < 1 { 29 | totalRingPayment += citem.Item.Amount 30 | } 31 | } 32 | } 33 | return totalRingPayment 34 | } 35 | -------------------------------------------------------------------------------- /logic/generators.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | 7 | "github.com/RunnersRevival/outrun/netobj" 8 | ) 9 | 10 | // GenerateLoginPasskey is used by LoginDelta to verify the login passkey sent by the game. 11 | func GenerateLoginPasskey(player netobj.Player) string { 12 | data := []byte(player.Key + ":dho5v5yy7n2uswa5iblb:" + player.ID + ":" + player.Password) 13 | sum := md5.Sum(data) 14 | return hex.EncodeToString(sum[:]) 15 | } 16 | -------------------------------------------------------------------------------- /logic/wheelRefreshLogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/config/campaignconf" 5 | "github.com/RunnersRevival/outrun/consts" 6 | "github.com/RunnersRevival/outrun/enums" 7 | "github.com/RunnersRevival/outrun/logic/conversion" 8 | "github.com/RunnersRevival/outrun/netobj" 9 | "github.com/RunnersRevival/outrun/obj" 10 | ) 11 | 12 | func WheelRefreshLogic(player netobj.Player, wheel netobj.WheelOptions) netobj.WheelOptions { 13 | freeSpins := consts.RouletteFreeSpins 14 | campaignList := []obj.Campaign{} 15 | if campaignconf.CFile.AllowCampaigns { 16 | for _, confCampaign := range campaignconf.CFile.CurrentCampaigns { 17 | newCampaign := conversion.ConfiguredCampaignToCampaign(confCampaign) 18 | campaignList = append(campaignList, newCampaign) 19 | } 20 | } 21 | for index := range campaignList { 22 | if obj.IsCampaignActive(campaignList[index]) && campaignList[index].Type == enums.CampaignTypeFreeWheelSpinCount { 23 | freeSpins = campaignList[index].Content 24 | } 25 | index++ 26 | } 27 | 28 | // TODO: Find a more standard way of refreshing the wheel status, because this is scary code 29 | numRouletteTicket := player.PlayerState.NumRouletteTicket // get roulette tickets 30 | rouletteCount := player.RouletteInfo.RouletteCountInPeriod // get amount of times we've spun the wheel today 31 | if player.RouletteInfo.GotJackpotThisPeriod { 32 | wheel.NumJackpotRing = 1 33 | } 34 | wheel.NumRouletteToken = numRouletteTicket 35 | wheel.NumRemainingRoulette = wheel.NumRouletteToken + freeSpins - rouletteCount // TODO: is this proper? 36 | if wheel.NumRemainingRoulette < wheel.NumRouletteToken { 37 | wheel.NumRemainingRoulette = wheel.NumRouletteToken 38 | } 39 | 40 | return wheel 41 | } 42 | -------------------------------------------------------------------------------- /meta/versions.go: -------------------------------------------------------------------------------- 1 | package meta 2 | 3 | const ( 4 | // Version format: .. 5 | Version = "1.1.1r" 6 | ) 7 | -------------------------------------------------------------------------------- /muxhandlers/friend.go: -------------------------------------------------------------------------------- 1 | package muxhandlers 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/emess" 5 | "github.com/RunnersRevival/outrun/helper" 6 | "github.com/RunnersRevival/outrun/responses" 7 | "github.com/RunnersRevival/outrun/status" 8 | ) 9 | 10 | func GetFacebookIncentive(helper *helper.Helper) { 11 | // We respond with no presents for now. 12 | player, err := helper.GetCallingPlayer() 13 | if err != nil { 14 | helper.InternalErr("Error getting calling player", err) 15 | return 16 | } 17 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 18 | response := responses.DefaultFacebookIncentive(baseInfo, player) 19 | err = helper.SendResponse(response) 20 | if err != nil { 21 | helper.InternalErr("Error sending response", err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /muxhandlers/leaderboard.go: -------------------------------------------------------------------------------- 1 | package muxhandlers 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/RunnersRevival/outrun/emess" 7 | "github.com/RunnersRevival/outrun/helper" 8 | "github.com/RunnersRevival/outrun/requests" 9 | "github.com/RunnersRevival/outrun/responses" 10 | "github.com/RunnersRevival/outrun/status" 11 | ) 12 | 13 | func GetWeeklyLeaderboardOptions(helper *helper.Helper) { 14 | recv := helper.GetGameRequest() 15 | var request requests.LeaderboardRequest 16 | err := json.Unmarshal(recv, &request) 17 | if err != nil { 18 | helper.Err("Error unmarshalling", err) 19 | return 20 | } 21 | mode := request.Mode 22 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 23 | response := responses.DefaultWeeklyLeaderboardOptions(baseInfo, mode) 24 | err = helper.SendResponse(response) 25 | if err != nil { 26 | helper.InternalErr("Error sending response", err) 27 | } 28 | } 29 | 30 | func GetWeeklyLeaderboardEntries(helper *helper.Helper) { 31 | recv := helper.GetGameRequest() 32 | var request requests.LeaderboardEntriesRequest 33 | err := json.Unmarshal(recv, &request) 34 | if err != nil { 35 | helper.Err("Error unmarshalling", err) 36 | return 37 | } 38 | player, err := helper.GetCallingPlayer() 39 | if err != nil { 40 | helper.InternalErr("Error getting calling player", err) 41 | return 42 | } 43 | mode := request.Mode 44 | lbtype := request.Type 45 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 46 | response := responses.DefaultWeeklyLeaderboardEntries(baseInfo, player, mode, lbtype) 47 | err = helper.SendResponse(response) 48 | if err != nil { 49 | helper.InternalErr("Error sending response", err) 50 | } 51 | } 52 | 53 | func GetLeagueData(helper *helper.Helper) { 54 | recv := helper.GetGameRequest() 55 | var request requests.LeaderboardRequest 56 | err := json.Unmarshal(recv, &request) 57 | if err != nil { 58 | helper.Err("Error unmarshalling", err) 59 | return 60 | } 61 | mode := request.Mode 62 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 63 | response := responses.DefaultLeagueData(baseInfo, mode) 64 | err = helper.SendResponse(response) 65 | if err != nil { 66 | helper.InternalErr("Error sending response", err) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /muxhandlers/muxobj/objMuxHandler.go: -------------------------------------------------------------------------------- 1 | package muxobj 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "reflect" 7 | "runtime" 8 | "strconv" 9 | "strings" 10 | "time" 11 | 12 | "github.com/RunnersRevival/outrun/config" 13 | "github.com/RunnersRevival/outrun/helper" 14 | ) 15 | 16 | var shuttingDown = false 17 | 18 | func Handle(f func(*helper.Helper), logExecutionTime bool) func(w http.ResponseWriter, r *http.Request) { 19 | funcnameSplit := strings.Split(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name(), "/") 20 | funcname := funcnameSplit[len(funcnameSplit)-1] 21 | funcnameSplit = strings.Split(funcname, ".") // just get function name 22 | funcname = funcnameSplit[len(funcnameSplit)-1] 23 | if shuttingDown { 24 | // prevent any requests from going through in order to stop any possible edits to the database while Outrun is shutting down 25 | return func(w http.ResponseWriter, r *http.Request) { 26 | w.WriteHeader(http.StatusServiceUnavailable) 27 | w.Write([]byte("Service Unavailable")) 28 | } 29 | } else { 30 | if logExecutionTime { 31 | return func(w http.ResponseWriter, r *http.Request) { 32 | startTime := time.Now() 33 | help := helper.MakeHelper(funcname, w, r) 34 | if config.CFile.LogAllRequests { 35 | nano := time.Now().UnixNano() 36 | nanoStr := strconv.Itoa(int(nano)) 37 | filename := help.Request.RequestURI + "--" + nanoStr 38 | filename = strings.ReplaceAll(filename, ".", "-") 39 | filename = strings.ReplaceAll(filename, "/", "-") + ".txt" 40 | filepath := "logging/all_requests/" + filename 41 | help.Out("DEBUG: Saving request to " + filepath) 42 | err := ioutil.WriteFile(filepath, help.GetGameRequest(), 0644) 43 | if err != nil { 44 | help.Out("DEBUG ERROR: Unable to write file '" + filepath + "'") 45 | } 46 | } 47 | f(help) 48 | endTime := time.Now() 49 | timeDiff := endTime.Sub(startTime) 50 | help.Out("Done executing in " + timeDiff.String()) 51 | } 52 | } else { 53 | return func(w http.ResponseWriter, r *http.Request) { 54 | help := helper.MakeHelper(funcname, w, r) 55 | f(help) 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /muxhandlers/option.go: -------------------------------------------------------------------------------- 1 | package muxhandlers 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/emess" 5 | "github.com/RunnersRevival/outrun/helper" 6 | "github.com/RunnersRevival/outrun/responses" 7 | "github.com/RunnersRevival/outrun/status" 8 | ) 9 | 10 | func GetOptionUserResult(helper *helper.Helper) { 11 | player, err := helper.GetCallingPlayer() 12 | if err != nil { 13 | helper.InternalErr("Error getting calling player", err) 14 | return 15 | } 16 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 17 | response := responses.OptionUserResult(baseInfo, player.OptionUserResult) 18 | helper.SendResponse(response) 19 | } 20 | -------------------------------------------------------------------------------- /muxhandlers/raidbossSpin.go: -------------------------------------------------------------------------------- 1 | package muxhandlers 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/emess" 5 | "github.com/RunnersRevival/outrun/helper" 6 | "github.com/RunnersRevival/outrun/responses" 7 | "github.com/RunnersRevival/outrun/status" 8 | ) 9 | 10 | func GetItemStockNum(helper *helper.Helper) { 11 | // TODO: Flesh out properly! The game responds with 12 | // [IDRouletteTicketPremium, IDRouletteTicketItem, IDSpecialEgg] 13 | // for item IDs, along with an event ID, likely for event characters. 14 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 15 | response := responses.DefaultItemStockNum(baseInfo) 16 | err := helper.SendResponse(response) 17 | if err != nil { 18 | helper.InternalErr("Error sending response", err) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /muxhandlers/sgn.go: -------------------------------------------------------------------------------- 1 | package muxhandlers 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/RunnersRevival/outrun/emess" 7 | "github.com/RunnersRevival/outrun/helper" 8 | "github.com/RunnersRevival/outrun/requests" 9 | "github.com/RunnersRevival/outrun/responses" 10 | "github.com/RunnersRevival/outrun/status" 11 | ) 12 | 13 | func SendApollo(helper *helper.Helper) { 14 | data := helper.GetGameRequest() 15 | var request requests.Base 16 | err := json.Unmarshal(data, &request) 17 | if err != nil { 18 | helper.InternalErr("Error unmarshalling", err) 19 | return 20 | } 21 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 22 | response := responses.NewBaseResponseV(baseInfo, request.Version) 23 | err = helper.SendResponse(response) 24 | if err != nil { 25 | helper.InternalErr("Error sending response", err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /muxhandlers/store.go: -------------------------------------------------------------------------------- 1 | package muxhandlers 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | 7 | "github.com/RunnersRevival/outrun/analytics" 8 | "github.com/RunnersRevival/outrun/analytics/factors" 9 | "github.com/RunnersRevival/outrun/db" 10 | "github.com/RunnersRevival/outrun/emess" 11 | "github.com/RunnersRevival/outrun/helper" 12 | "github.com/RunnersRevival/outrun/obj" 13 | "github.com/RunnersRevival/outrun/obj/constobjs" 14 | "github.com/RunnersRevival/outrun/requests" 15 | "github.com/RunnersRevival/outrun/responses" 16 | "github.com/RunnersRevival/outrun/status" 17 | ) 18 | 19 | func GetRedStarExchangeList(helper *helper.Helper) { 20 | recv := helper.GetGameRequest() 21 | var request requests.RedStarExchangeListRequest 22 | err := json.Unmarshal(recv, &request) 23 | if err != nil { 24 | helper.Err("Error unmarshalling", err) 25 | return 26 | } 27 | 28 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 29 | var response responses.RedStarExchangeListResponse 30 | var redStarItems []obj.RedStarItem 31 | helper.Out("Recv ItemType " + strconv.Itoa(int(request.ItemType))) 32 | if request.ItemType == 0 { 33 | redStarItems = []obj.RedStarItem{} 34 | } else if request.ItemType == 1 { 35 | redStarItems = constobjs.RedStarItemsType1 36 | } else if request.ItemType == 2 { 37 | redStarItems = constobjs.RedStarItemsType2 38 | } else if request.ItemType == 4 { 39 | redStarItems = constobjs.RedStarItemsType4 40 | } else { 41 | helper.Respond([]byte("Invalid request")) 42 | return 43 | } 44 | 45 | response = responses.RedStarExchangeList(baseInfo, redStarItems, 0, "1900-1-1") 46 | err = helper.SendResponse(response) 47 | if err != nil { 48 | helper.InternalErr("Error sending response", err) 49 | } 50 | } 51 | 52 | func RedStarExchange(helper *helper.Helper) { 53 | recv := helper.GetGameRequest() 54 | var request requests.RedStarExchange 55 | err := json.Unmarshal(recv, &request) 56 | if err != nil { 57 | helper.Err("Error unmarshalling", err) 58 | return 59 | } 60 | 61 | player, err := helper.GetCallingPlayer() 62 | if err != nil { 63 | helper.InternalErr("Error getting calling player", err) 64 | return 65 | } 66 | 67 | baseInfo := helper.BaseInfo(emess.OK, status.OK) 68 | itemID := request.ItemID 69 | getItemType := func(iid string) (string, int64, bool) { 70 | var itemType string 71 | itemPrice, ok := constobjs.ShopRingPrices[iid] 72 | if !ok { 73 | // it's not a ring item 74 | itemPrice, ok = constobjs.ShopEnergyPrices[iid] 75 | if !ok { 76 | // it's not ring nor energy item 77 | // unrecognized item! 78 | return "", 0, false 79 | } 80 | itemType = "energy" 81 | return itemType, itemPrice, true 82 | } 83 | itemType = "ring" 84 | return itemType, itemPrice, true 85 | } 86 | 87 | itemType, itemPrice, found := getItemType(itemID) 88 | if found { 89 | switch itemType { 90 | case "ring": 91 | if player.PlayerState.NumRedRings-itemPrice < 0 { 92 | baseInfo.StatusCode = status.NotEnoughRedRings 93 | return 94 | } 95 | player.PlayerState.NumRedRings -= itemPrice 96 | player.PlayerState.NumRings += constobjs.ShopRingAmounts[itemID] 97 | db.SavePlayer(player) 98 | _, err = analytics.Store(player.ID, factors.AnalyticTypePurchaseRings) 99 | if err != nil { 100 | helper.WarnErr("Error storing analytics (AnalyticTypePurchaseRings)", err) 101 | } 102 | case "energy": 103 | if player.PlayerState.NumRedRings-itemPrice < 0 { 104 | baseInfo.StatusCode = status.NotEnoughRedRings 105 | return 106 | } 107 | player.PlayerState.NumRedRings -= itemPrice 108 | player.PlayerState.Energy += constobjs.ShopEnergyAmounts[itemID] 109 | db.SavePlayer(player) 110 | _, err = analytics.Store(player.ID, factors.AnalyticTypePurchaseEnergy) 111 | if err != nil { 112 | helper.WarnErr("Error storing analytics (AnalyticTypePurchaseEnergy)", err) 113 | } 114 | default: 115 | // this should never execute! 116 | baseInfo.StatusCode = status.MasterDataMismatch 117 | helper.Out("Default case executed... Something went wrong!") 118 | } 119 | } else { 120 | baseInfo.StatusCode = status.MasterDataMismatch 121 | } 122 | 123 | response := responses.DefaultRedStarExchange(baseInfo, player) 124 | err = helper.SendResponse(response) 125 | if err != nil { 126 | helper.InternalErr("Error sending response", err) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /netobj/battleState.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | "github.com/jinzhu/now" 6 | 7 | "time" 8 | ) 9 | 10 | type BattleState struct { 11 | ScoreRecordedToday bool `json:"hasRecordedScoreToday"` 12 | DailyBattleHighScore int64 `json:"maxScore"` 13 | PrevDailyBattleHighScore int64 `json:"lastMaxScore"` 14 | BattleStartsAt int64 `json:"startTime"` 15 | BattleEndsAt int64 `json:"expireTime"` 16 | MatchedUpWithRival bool `json:"matchedUpWithRival"` 17 | RivalID string `json:"rivalId"` 18 | Wins int64 `json:"numWin"` 19 | Losses int64 `json:"numLose"` 20 | Draws int64 `json:"numDraw"` // very rarely gets incremented, but game expects it so it's here 21 | Failures int64 `json:"numLoseByDefault"` 22 | WinStreak int64 `json:"goOnWin"` 23 | LossStreak int64 `json:"goOnLosses"` 24 | BattleHistory []obj.BattlePair `json:"battleDataHistory"` 25 | PendingReward bool `json:"pendingReward"` 26 | PendingRewardData obj.RewardBattlePair `json:"pendingRewardData"` 27 | WantsStricterMatchmaking bool `json:"wantsStricterMatchmaking"` 28 | NextRerollAvailableAt int64 `json:"nextRerollAvailableAt"` 29 | } 30 | 31 | func NewBattleState(scoreRecordedToday bool, dailyBattleHighScore, prevDailyBattleHighScore, battleStartTime, battleEndTime int64, matchedUpWithRival bool, rivalID string, wins, losses, draws, failures, winStreak, lossStreak int64, battleHistory []obj.BattlePair, pendingReward bool, pendingRewardData obj.RewardBattlePair, wantsStricterMatchmaking bool, nextRerollAvailableAt int64) BattleState { 32 | return BattleState{ 33 | scoreRecordedToday, 34 | dailyBattleHighScore, 35 | prevDailyBattleHighScore, 36 | battleStartTime, 37 | battleEndTime, 38 | matchedUpWithRival, 39 | rivalID, 40 | wins, 41 | losses, 42 | draws, 43 | failures, 44 | winStreak, 45 | lossStreak, 46 | battleHistory, 47 | pendingReward, 48 | pendingRewardData, 49 | wantsStricterMatchmaking, 50 | nextRerollAvailableAt, 51 | } 52 | } 53 | 54 | func DefaultBattleState() BattleState { 55 | scoreRecordedToday := false 56 | dailyBattleHighScore := int64(0) 57 | prevDailyBattleHighScore := int64(0) 58 | battleStartTime := now.BeginningOfDay().UTC().Unix() 59 | battleEndTime := now.EndOfDay().UTC().Unix() + 1 60 | matchedUpWithRival := false 61 | rivalID := "" 62 | wins := int64(0) 63 | losses := int64(0) 64 | draws := int64(0) 65 | failures := int64(0) 66 | winStreak := int64(0) 67 | lossStreak := int64(0) 68 | battleHistory := []obj.BattlePair{} 69 | pendingReward := false 70 | pendingRewardData := obj.NewRewardBattlePair(-1, -1, obj.DebugRivalBattleData(), obj.DebugRivalBattleData()) //dummy data 71 | wantsStricterMatchmaking := false 72 | nextRerollAvailableAt := time.Now().UTC().Unix() 73 | return NewBattleState( 74 | scoreRecordedToday, 75 | dailyBattleHighScore, 76 | prevDailyBattleHighScore, 77 | battleStartTime, 78 | battleEndTime, 79 | matchedUpWithRival, 80 | rivalID, 81 | wins, 82 | losses, 83 | draws, 84 | failures, 85 | winStreak, 86 | lossStreak, 87 | battleHistory, 88 | pendingReward, 89 | pendingRewardData, 90 | wantsStricterMatchmaking, 91 | nextRerollAvailableAt, 92 | ) 93 | } 94 | -------------------------------------------------------------------------------- /netobj/chao.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | ) 6 | 7 | type Chao struct { 8 | obj.Chao 9 | Status int64 `json:"status"` // enums.ChaoStatus* 10 | Level int64 `json:"level"` 11 | Dealing int64 `json:"setStatus"` // enums.ChaoDealing* 12 | Acquired int64 `json:"acquired"` // flag 13 | } 14 | 15 | func NewNetChao(chao obj.Chao, status, level, dealing, acquired int64) Chao { 16 | return Chao{ 17 | chao, 18 | status, 19 | level, 20 | dealing, 21 | acquired, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /netobj/chaoRouletteGroup.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/logic/roulette" 5 | ) 6 | 7 | type ChaoRouletteGroup struct { 8 | ChaoWheelOptions ChaoWheelOptions `json:"ORN_lastChaoWheelOptions"` // actual wheel options for this wheel 9 | WheelChao []string `json:"ORN_wheelChao"` // what Chao/characters are in this wheel 10 | ChaoRouletteInfo RouletteInfo `json:"ORN_chaoRouletteInfo"` // may not be needed 11 | } 12 | 13 | func DefaultChaoRouletteGroup(playerState PlayerState, allowedCharacters, allowedChao []string) ChaoRouletteGroup { 14 | chaoWheelOptions := DefaultChaoWheelOptions(playerState) 15 | //wheelChao, err := roulette.GetRandomChaoRouletteItems(chaoWheelOptions.Rarity, exclusions) // populate based on given rarities 16 | wheelChao, newRarity, err := roulette.GetRandomChaoRouletteItems(chaoWheelOptions.Rarity, allowedCharacters, allowedChao) // populate based on given rarities 17 | if err != nil { 18 | panic(err) // TODO: Find a better way to handle error. Hard to manage since the player creators don't already output errors 19 | } 20 | // newRarity is rarity but with any modifications that need to be made 21 | chaoWheelOptions.Rarity = newRarity 22 | chaoRouletteInfo := DefaultRouletteInfo() 23 | return ChaoRouletteGroup{ 24 | chaoWheelOptions, 25 | wheelChao, 26 | chaoRouletteInfo, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /netobj/chaoSpinPrize.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type ChaoSpinPrize struct { 9 | ID string `json:"chaoId"` 10 | Level int64 `json:"level"` 11 | Rarity int64 `json:"rarity"` 12 | } 13 | 14 | func NetChaoToChaoSpinPrize(chao Chao) ChaoSpinPrize { 15 | id := chao.ID 16 | level := chao.Level 17 | rarity := chao.Rarity 18 | return ChaoSpinPrize{ 19 | id, 20 | level, 21 | rarity, 22 | } 23 | } 24 | 25 | func ChaoIDToChaoSpinPrize(chid string) ChaoSpinPrize { 26 | id := chid 27 | level := int64(0) // TODO: check if the game is accepting of this value... 28 | rarityInt, err := strconv.Atoi(string(chid[2])) // rarity is third digit 29 | if err != nil { 30 | panic(err) // TODO: handle better 31 | } 32 | rarity := int64(rarityInt) 33 | return ChaoSpinPrize{ 34 | id, 35 | level, 36 | rarity, 37 | } 38 | } 39 | 40 | func CharacterToChaoSpinPrize(char Character) ChaoSpinPrize { 41 | id := char.ID 42 | level := char.Level 43 | rarity := int64(100) // used so the game recognizes that it is a character 44 | return ChaoSpinPrize{ 45 | id, 46 | level, 47 | rarity, 48 | } 49 | } 50 | 51 | func CharacterIDToChaoSpinPrize(cid string) ChaoSpinPrize { 52 | id := cid 53 | level := int64(0) // TODO: check if the game is accepting of this value... 54 | rarity := int64(100) // used so the game recognizes that it is a character 55 | return ChaoSpinPrize{ 56 | id, 57 | level, 58 | rarity, 59 | } 60 | } 61 | 62 | func GenericIDToChaoSpinPrize(id string) ChaoSpinPrize { 63 | idStarter := string(id[0]) 64 | if idStarter == "3" { // Character ID 65 | return CharacterIDToChaoSpinPrize(id) 66 | } else if idStarter == "4" { // Chao ID 67 | return ChaoIDToChaoSpinPrize(id) 68 | } else { 69 | // TODO: Gracefully handle error 70 | err := fmt.Errorf("invalid ID '%v'", id) 71 | panic(err) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /netobj/chaoSpinResult.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import "github.com/RunnersRevival/outrun/obj" 4 | 5 | type ChaoSpinResult struct { 6 | WonPrize ChaoSpinPrize `json:"getChao"` // chao or character 7 | ItemList []obj.Item `json:"itemList"` // TODO: what does this do? 8 | ItemWon int64 `json:"itemWon"` // probably index of item in ItemList 9 | } 10 | 11 | func DefaultChaoSpinResultNoItems(wonPrize ChaoSpinPrize) ChaoSpinResult { 12 | return ChaoSpinResult{ 13 | wonPrize, 14 | []obj.Item{}, 15 | -1, // TODO: if something breaks, it might be because of this 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /netobj/chaoWheelOptions.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/consts" 5 | "github.com/RunnersRevival/outrun/enums" 6 | "github.com/RunnersRevival/outrun/obj" 7 | "github.com/jinzhu/now" 8 | ) 9 | 10 | type ChaoWheelOptions struct { 11 | Rarity []int64 `json:"rarity"` 12 | ItemWeight []int64 `json:"itemWeight"` 13 | CampaignList []obj.Campaign `json:"campaignList"` 14 | SpinCost int64 `json:"spinCost"` 15 | ChaoRouletteType int64 `json:"chaoRouletteType"` // value from enums.ChaoWheelType* 16 | NumSpecialEgg int64 `json:"numSpecialEgg"` 17 | RouletteAvailable int64 `json:"rouletteAvailable"` // flag 18 | NumChaoRouletteToken int64 `json:"numChaoRouletteToken"` // number of premium roulette tickets 19 | NumChaoRoulette int64 `json:"numChaoRoulette"` // == 0 --> chaoWheelOptions.IsTutorial 20 | StartTime int64 `json:"startTime"` // TODO: Is this needed? 21 | EndTime int64 `json:"endTime"` // TODO: Is this needed? 22 | } 23 | 24 | func NewChaoWheelOptions(rarity, itemWeight []int64, campaignList []obj.Campaign, spinCost, chaoRouletteType, numSpecialEgg, rouletteAvailable, numChaoRouletteToken, numChaoRoulette, startTime, endTime int64) ChaoWheelOptions { 25 | return ChaoWheelOptions{ 26 | rarity, 27 | itemWeight, 28 | campaignList, 29 | spinCost, 30 | chaoRouletteType, 31 | numSpecialEgg, 32 | rouletteAvailable, 33 | numChaoRouletteToken, 34 | numChaoRoulette, 35 | startTime, 36 | endTime, 37 | } 38 | } 39 | 40 | func DefaultChaoWheelOptions(playerState PlayerState) ChaoWheelOptions { 41 | rarity := []int64{2, 1, 100, 0, 2, 0, 100, 1} 42 | //rarity := []int64{2, 1, 100, 1, 2, 1, 100, 1} // old layout 43 | //rarity := []int64{0, 1, 2, 100, 0, 1, 2, 100} // temporary placeholder layout 44 | itemWeight := []int64{6, 17, 5, 17, 16, 17, 5, 17} // Could possibly fake these, but the logic shouldn't allow it to happen 45 | campaignList := []obj.Campaign{} 46 | chaoRouletteType := enums.ChaoWheelTypeNormal 47 | numSpecialEgg := playerState.ChaoEggs 48 | if numSpecialEgg >= 10 { 49 | chaoRouletteType = enums.ChaoWheelTypeSpecial 50 | rarity = []int64{2, 1, 100, 2, 1, 2, 100, 1} 51 | itemWeight = []int64{1, 1, 1, 1, 1, 1, 1, 1} 52 | } 53 | rouletteAvailable := int64(1) 54 | numChaoRouletteToken := playerState.NumChaoRouletteTicket 55 | spinCost := consts.ChaoRouletteTicketCost 56 | if numChaoRouletteToken <= 0 { // if out of chao roulette tickets 57 | // use higher value for the red rings 58 | spinCost = consts.ChaoRouletteRedRingCost 59 | } 60 | numChaoRoulette := int64(1) 61 | startTime := now.BeginningOfDay().UTC().Unix() + 32400 // 12 AM + 9 hours = 9 AM 62 | endTime := startTime + 86399 // 23:59:59 later 63 | return NewChaoWheelOptions(rarity, itemWeight, campaignList, spinCost, chaoRouletteType, numSpecialEgg, rouletteAvailable, numChaoRouletteToken, numChaoRoulette, startTime, endTime) 64 | } 65 | -------------------------------------------------------------------------------- /netobj/constnetobjs/allNetChao.go: -------------------------------------------------------------------------------- 1 | package constnetobjs 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/config/gameconf" 7 | "github.com/RunnersRevival/outrun/enums" 8 | "github.com/RunnersRevival/outrun/netobj" 9 | "github.com/RunnersRevival/outrun/obj" 10 | ) 11 | 12 | var ChaoIDs = []string{"400000", "400001", "400002", "400003", "400004", "400005", "400006", "400007", "400008", "400009", "400010", "400011", "400012", "400013", "400014", "400015", "400016", "400017", "400018", "400019", "400020", "400021", "400022", "400023", "400024", "400025", "401000", "401001", "401002", "401003", "401004", "401005", "401006", "401007", "401008", "401009", "401010", "401011", "401012", "401013", "401014", "401015", "401016", "401017", "401018", "401019", "401020", "401021", "401022", "401023", "401024", "401025", "401026", "401027", "401028", "401029", "401030", "401031", "401032", "401033", "401034", "401035", "401036", "401037", "401038", "401039", "401040", "401041", "401042", "401043", "401044", "401045", "401046", "401047", "402000", "402001", "402002", "402003", "402004", "402005", "402006", "402007", "402008", "402009", "402010", "402011", "402012", "402013", "402014", "402015", "402016", "402017", "402018", "402019", "402020", "402021", "402022", "402023", "402024", "402025", "402026", "402027", "402028", "402029", "402030", "402031", "402032", "402033", "402034"} 13 | 14 | var NetChao = GetAllNetChao() 15 | var NetChaoList = GetAllNetChaoList() 16 | 17 | func DefaultChaoState() []netobj.Chao { 18 | chaos := []netobj.Chao{} 19 | chaoStatus := int64(enums.ChaoStatusNotOwned) 20 | chaoLevel := int64(0) 21 | chaoDealing := int64(enums.ChaoDealingNone) // TODO: discover use 22 | acquired := int64(0) 23 | if gameconf.CFile.AllChaoUnlocked { 24 | chaoStatus = enums.ChaoStatusOwned 25 | acquired = int64(1) 26 | } 27 | for _, chaoID := range ChaoIDs { 28 | id := chaoID 29 | rarity, _ := strconv.Atoi(string(id[2])) // numerical rarity (third digit) 30 | hidden := int64(0) // TODO: discover what this is used for (see obj/chao.go) 31 | chao := obj.NewChao( 32 | id, 33 | int64(rarity), 34 | hidden, 35 | ) 36 | netchao := netobj.NewNetChao( 37 | chao, 38 | chaoStatus, 39 | chaoLevel, 40 | chaoDealing, 41 | acquired, 42 | ) 43 | chaos = append(chaos, netchao) 44 | } 45 | return chaos 46 | } 47 | 48 | func UnlockedChaoState() []netobj.Chao { 49 | chaos := []netobj.Chao{} 50 | chaoStatus := int64(enums.ChaoStatusOwned) 51 | chaoLevel := int64(0) 52 | chaoDealing := int64(enums.ChaoDealingNone) // TODO: discover use 53 | acquired := int64(1) 54 | for _, chaoID := range ChaoIDs { 55 | id := chaoID 56 | rarity, _ := strconv.Atoi(string(id[2])) // numerical rarity (third digit) 57 | hidden := int64(0) // TODO: discover what this is used for (see obj/chao.go) 58 | chao := obj.NewChao( 59 | id, 60 | int64(rarity), 61 | hidden, 62 | ) 63 | netchao := netobj.NewNetChao( 64 | chao, 65 | chaoStatus, 66 | chaoLevel, 67 | chaoDealing, 68 | acquired, 69 | ) 70 | chaos = append(chaos, netchao) 71 | } 72 | return chaos 73 | } 74 | 75 | func GetAllNetChao() map[string]netobj.Chao { 76 | // TODO: remove. Should not be used anymore. 77 | chaos := make(map[string]netobj.Chao) 78 | for _, chaoID := range ChaoIDs { 79 | id := chaoID 80 | rarity, _ := strconv.Atoi(string(id[2])) // numerical rarity (third digit) 81 | hidden := int64(0) // TODO: discover what this is used for (see obj/chao.go) 82 | chao := obj.NewChao( 83 | id, 84 | int64(rarity), 85 | hidden, 86 | ) 87 | netchao := netobj.NewNetChao( 88 | chao, 89 | enums.ChaoStatusOwned, 90 | 1, 91 | enums.ChaoDealingNone, 92 | 1, 93 | ) 94 | chaos[chaoID] = netchao 95 | } 96 | return chaos 97 | } 98 | 99 | func GetAllNetChaoList() []netobj.Chao { 100 | chaolist := []netobj.Chao{} 101 | for _, value := range GetAllNetChao() { 102 | chaolist = append(chaolist, value) 103 | } 104 | return chaolist 105 | } 106 | -------------------------------------------------------------------------------- /netobj/constnetobjs/blankPlayer.go: -------------------------------------------------------------------------------- 1 | package constnetobjs 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | 7 | "github.com/RunnersRevival/outrun/obj" 8 | 9 | "github.com/RunnersRevival/outrun/config/eventconf" 10 | "github.com/RunnersRevival/outrun/consts" 11 | "github.com/RunnersRevival/outrun/enums" 12 | "github.com/RunnersRevival/outrun/netobj" 13 | ) 14 | 15 | var BlankPlayer = func() netobj.Player { 16 | randChar := func(charset string, length int64) string { 17 | runes := []rune(charset) 18 | final := make([]rune, 10) 19 | for i := range final { 20 | final[i] = runes[rand.Intn(len(runes))] 21 | } 22 | return string(final) 23 | } 24 | // create ID 25 | uid := "" 26 | for i := range make([]byte, 10) { 27 | if i == 0 { // if first character 28 | uid += strconv.Itoa(rand.Intn(9) + 1) 29 | } else { 30 | uid += strconv.Itoa(rand.Intn(10)) 31 | } 32 | } 33 | username := "" 34 | password := randChar("abcdefghijklmnopqrstuvwxyz1234567890", 10) 35 | migrationPassword := randChar("abcdefghijklmnopqrstuvwxyz1234567890", 10) 36 | userPassword := "" 37 | key := randChar("abcdefghijklmnopqrstuvwxyz1234567890", 10) 38 | playerState := netobj.DefaultPlayerState() 39 | characterState := netobj.DefaultCharacterState() 40 | chaoState := GetAllNetChaoList() // needed for chaoRouletteAllowed 41 | mileageMapState := netobj.DefaultMileageMapState() 42 | mileageFriends := []netobj.MileageFriend{} 43 | playerVarious := netobj.DefaultPlayerVarious() 44 | optionUserResult := netobj.DefaultOptionUserResult() 45 | rouletteInfo := netobj.DefaultRouletteInfo() 46 | wheelOptions := netobj.DefaultWheelOptions(playerState.NumRouletteTicket, rouletteInfo.RouletteCountInPeriod, enums.WheelRankNormal, consts.RouletteFreeSpins) 47 | // TODO: get rid of logic here? 48 | allowedCharacters := []string{} 49 | allowedChao := []string{} 50 | for _, chao := range chaoState { 51 | if chao.Level < 10 { // not max level 52 | allowedChao = append(allowedChao, chao.ID) 53 | } 54 | } 55 | for _, character := range characterState { 56 | if character.Star < 10 { // not max star 57 | allowedCharacters = append(allowedCharacters, character.ID) 58 | } 59 | } 60 | chaoRouletteGroup := netobj.DefaultChaoRouletteGroup(playerState, allowedCharacters, allowedChao) 61 | personalEvents := []eventconf.ConfiguredEvent{} 62 | messages := []obj.Message{} 63 | operatorMessages := []obj.OperatorMessage{} 64 | loginBonusState := netobj.DefaultLoginBonusState(0) 65 | eventState := netobj.DefaultEventState() 66 | battleState := netobj.DefaultBattleState() 67 | disallowInactivePurge := false 68 | lastLoginPlatformID := int64(0) 69 | return netobj.NewPlayer( 70 | uid, 71 | username, 72 | password, 73 | migrationPassword, 74 | userPassword, 75 | key, 76 | playerState, 77 | characterState, 78 | chaoState, 79 | mileageMapState, 80 | mileageFriends, 81 | playerVarious, 82 | optionUserResult, 83 | wheelOptions, 84 | rouletteInfo, 85 | chaoRouletteGroup, 86 | personalEvents, 87 | messages, 88 | operatorMessages, 89 | loginBonusState, 90 | false, 91 | eventState, 92 | 0, 93 | battleState, 94 | disallowInactivePurge, 95 | lastLoginPlatformID, 96 | ) 97 | }() // TODO: Solve duplication requirement with db/assistants.go 98 | -------------------------------------------------------------------------------- /netobj/eventState.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | type EventState struct { 4 | Param int64 `json:"param"` 5 | RewardID int64 `json:"rewardId"` // record-keeping, to see which reward ID we're currently on so we don't double-reward gifts 6 | } 7 | 8 | func DefaultEventState() EventState { 9 | param := int64(0) 10 | rewardID := int64(0) 11 | return NewEventState(param, rewardID) 12 | } 13 | 14 | func NewEventState(param, rewardID int64) EventState { 15 | return EventState{ 16 | param, 17 | rewardID, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /netobj/loginBonusState.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "github.com/jinzhu/now" 5 | ) 6 | 7 | type LoginBonusState struct { 8 | CurrentFirstLoginBonusDay int64 `json:"currentFirstLoginBonusDay"` // this doesn't get reset when the login bonus resets 9 | CurrentLoginBonusDay int64 `json:"currentLoginBonusDay"` 10 | LastLoginBonusTime int64 `json:"lastLoginBonusTime"` 11 | NextLoginBonusTime int64 `json:"nextLoginBonusTime"` 12 | LoginBonusStartTime int64 `json:"loginBonusStartTime"` 13 | LoginBonusEndTime int64 `json:"loginBonusEndTime"` 14 | } 15 | 16 | func NewLoginBonusState(cflbd, clbd, llbt, nlbt, lbst, lbet int64) LoginBonusState { 17 | return LoginBonusState{ 18 | cflbd, 19 | clbd, 20 | llbt, 21 | nlbt, 22 | lbst, 23 | lbet, 24 | } 25 | } 26 | 27 | func DefaultLoginBonusState(currentFirstLoginBonusDay int64) LoginBonusState { 28 | currentLoginBonusDay := int64(0) 29 | lastLoginBonusTime := int64(0) 30 | nextLoginBonusTime := int64(0) 31 | loginBonusStartTime := now.BeginningOfWeek().UTC().Unix() 32 | loginBonusEndTime := now.EndOfWeek().UTC().Unix() 33 | return NewLoginBonusState( 34 | currentFirstLoginBonusDay, 35 | currentLoginBonusDay, 36 | lastLoginBonusTime, 37 | nextLoginBonusTime, 38 | loginBonusStartTime, 39 | loginBonusEndTime, 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /netobj/mileageFriend.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | type MileageFriend struct { 4 | ID string `json:"friendId"` 5 | Name string `json:"name"` 6 | URL string `json:"url"` 7 | MileageMapState MileageMapState `json:"mapState"` 8 | } 9 | -------------------------------------------------------------------------------- /netobj/mileageMapState.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/obj" 7 | ) 8 | 9 | type MileageMapState struct { 10 | obj.MapInfo 11 | Episode int64 `json:"episode"` 12 | Chapter int64 `json:"chapter"` 13 | Point int64 `json:"point"` // point in episode 14 | StageTotalScore int64 `json:"stageTotalScore"` // TODO: discover use. This is very likely used for the total score gained in the current chapter, which means this value MUST be set to the total chapter score 15 | ChapterStartTime int64 `json:"chapterStartTime"` // TODO: discover use. Appears to be used for point item expiry? 16 | } 17 | 18 | func (m MileageMapState) AdvanceChapter() { 19 | m.StageTotalScore = int64(0) 20 | m.ChapterStartTime = time.Now().UTC().Unix() 21 | m.Chapter += 1 22 | m.Episode = int64(1) 23 | m.Point = int64(0) 24 | } 25 | func (m MileageMapState) AddScore(score int64) { 26 | m.StageTotalScore += score 27 | } 28 | 29 | func DefaultMileageMapState() MileageMapState { 30 | // implies that this is a new account 31 | mapInfo := obj.DefaultMapInfo() 32 | return MileageMapState{ 33 | mapInfo, 34 | 1, 35 | 1, 36 | 0, 37 | 0, 38 | time.Now().UTC().Unix(), 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /netobj/optionUserResult.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | type OptionUserResult struct { 4 | TotalSumHighScore int64 `json:"totalSumHightScore"` // highest total score recorded 5 | QuickTotalSumHighScore int64 `json:"quickTotalSumHightScore"` // same as above but for timed mode 6 | NumTakeAllRings int64 `json:"numTakeAllRings"` // total number of rings acquired ever 7 | NumTakeAllRedRings int64 `json:"numTakeAllRedRings"` // total number of red rings acquired ever 8 | NumChaoRoulette int64 `json:"numChaoRoulette"` // total times the chao roulette was spun 9 | NumItemRoulette int64 `json:"numItemRoulette"` // total times the item roulette was spun 10 | NumJackpot int64 `json:"numJackPot"` // total number of jackpots won ever 11 | NumMaximumJackpotRings int64 `json:"numMaximumJackPotRings"` // biggest jackpot won 12 | NumSupport int64 `json:"numSupport"` // ? 13 | } 14 | 15 | func DefaultOptionUserResult() OptionUserResult { 16 | totalSumHighScore := int64(0) 17 | quickTotalSumHighScore := int64(0) 18 | numTakeAllRings := int64(0) // TODO: should the starting rings and red rings count? 19 | numTakeAllRedRings := int64(0) 20 | numChaoRoulette := int64(0) 21 | numItemRoulette := int64(0) 22 | numJackpot := int64(0) 23 | numMaximumJackpotRings := int64(0) 24 | numSupport := int64(19191) 25 | return OptionUserResult{ 26 | totalSumHighScore, 27 | quickTotalSumHighScore, 28 | numTakeAllRings, 29 | numTakeAllRedRings, 30 | numChaoRoulette, 31 | numItemRoulette, 32 | numJackpot, 33 | numMaximumJackpotRings, 34 | numSupport, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /netobj/playCharacter.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | type PlayCharacter struct { 4 | /* 5 | // Unused for now. 6 | ID string `json:"characterId"` 7 | Star int64 `json:"star"` 8 | StarMax int64 `json:"starMax"` 9 | PriceRings int64 `json:"priceNumRings"` 10 | PriceRedRings int64 `json:"priceNumRedRings"` 11 | */ 12 | } 13 | -------------------------------------------------------------------------------- /netobj/playerVarious.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/config/gameconf" 5 | ) 6 | 7 | type PlayerVarious struct { 8 | CmSkipCount int64 `json:"cmSkipCount"` // no clear purpose 9 | EnergyRecoveryMax int64 `json:"energyRecoveryMax"` 10 | EnergyRecoveryTime int64 `json:"energyRecveryTime"` 11 | OnePlayCmCount int64 `json:"onePlayCmCount"` // number of ad continues per run??? no effect in 2.x 12 | OnePlayContinueCount int64 `json:"onePlayContinueCount"` // number of RSR continues per run 13 | IsPurchased int64 `json:"isPurchased"` // removes ads? doesn't seem to do anything in 2.x 14 | } 15 | 16 | func DefaultPlayerVarious() PlayerVarious { 17 | cmSkipCount := int64(5) 18 | energyRecoveryMax := gameconf.CFile.EnergyRecoveryMax 19 | energyRecoveryTime := gameconf.CFile.EnergyRecoveryTime 20 | onePlayCmCount := int64(0) 21 | onePlayContinueCount := int64(2) 22 | isPurchased := int64(0) 23 | return PlayerVarious{ 24 | cmSkipCount, 25 | energyRecoveryMax, 26 | energyRecoveryTime, 27 | onePlayCmCount, 28 | onePlayContinueCount, 29 | isPurchased, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /netobj/raidBossGroup.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | type RaidBossGroup struct { 4 | CurrentRaidBoss EventRaidbossState `json:"ORN_currentRaidBoss"` 5 | PreviousRaidBosses []EventRaidbossState `json:"ORN_previousRaidBosses` 6 | AverageRaidBossLevel int64 `json:"ORN_averageRaidBossLevel"` 7 | NextID int64 `json:"ORN_nextId"` 8 | } 9 | -------------------------------------------------------------------------------- /netobj/raidBossState.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import "time" 4 | 5 | type EventRaidbossState struct { 6 | ID int64 `json:"raidbossId"` 7 | Level int64 `json:"raidbossLevel"` 8 | Rarity int64 `json:"raidbossRarity"` // 0: Normal, 1: Rare, 2: Super-rare 9 | HP int64 `json:"raidbossHitPoint"` 10 | MaxHP int64 `json:"raidbossMaxHitPoint"` 11 | Status int64 `json:"raidbossStatus"` // TODO: research 12 | EscapeAt int64 `json:"raidbossEscapeAt"` // the time when the raid boss expires 13 | EncounterName string `json:"encounterName"` 14 | EncounterFlg int64 `json:"encounterFlg"` 15 | CrowdedFlg int64 `json:"crowdedFlg"` // raid boss is full? 16 | ParticipateCount int64 `json:"participateCount"` // number of people taking on this raid boss? 17 | } 18 | 19 | func NewRaidbossState(id, level, rarity int64, encounterName string) EventRaidbossState { 20 | maxHp := int64((15*(level+3) - 50) * (rarity + 1)) //TODO: Preliminary algorithm 21 | hp := maxHp 22 | status := int64(0) 23 | escapeAt := int64(time.Now().UTC().Unix() + 3600) // raid boss expires after 1 hour 24 | encounterFlg := int64(0) 25 | crowdedFlg := int64(0) 26 | participateCount := int64(0) 27 | return EventRaidbossState{ 28 | id, 29 | level, 30 | rarity, 31 | hp, 32 | maxHp, 33 | status, 34 | escapeAt, 35 | encounterName, 36 | encounterFlg, 37 | crowdedFlg, 38 | participateCount, 39 | } 40 | } 41 | 42 | func DefaultRaidbossState() EventRaidbossState { 43 | // TODO: establish as constants 44 | id := int64(0) 45 | level := int64(2) 46 | rarity := int64(0) 47 | encounterName := "System" 48 | return NewRaidbossState( 49 | id, 50 | level, 51 | rarity, 52 | encounterName, 53 | ) 54 | } 55 | 56 | func DefaultRaidbossState2() EventRaidbossState { 57 | // TODO: establish as constants 58 | id := int64(1) 59 | level := int64(4) 60 | rarity := int64(1) 61 | encounterName := "System" 62 | return NewRaidbossState( 63 | id, 64 | level, 65 | rarity, 66 | encounterName, 67 | ) 68 | } 69 | 70 | func DefaultRaidbossState3() EventRaidbossState { 71 | // TODO: establish as constants 72 | id := int64(2) 73 | level := int64(8) 74 | rarity := int64(2) 75 | encounterName := "System" 76 | return NewRaidbossState( 77 | id, 78 | level, 79 | rarity, 80 | encounterName, 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /netobj/raidbossWheelOptions.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "strconv" 7 | 8 | "github.com/RunnersRevival/outrun/config" 9 | "github.com/RunnersRevival/outrun/consts" 10 | "github.com/RunnersRevival/outrun/enums" 11 | "github.com/RunnersRevival/outrun/logic/roulette" 12 | "github.com/RunnersRevival/outrun/obj" 13 | "github.com/jinzhu/now" 14 | ) 15 | 16 | type RaidbossWheelOptions struct { 17 | Items []string `json:"items"` 18 | Item []int64 `json:"item"` 19 | ItemWeight []int64 `json:"itemWeight"` 20 | ItemWon int64 `json:"itemWon"` 21 | NextFreeSpin int64 `json:"nextFreeSpin"` // midnight (start of next day) 22 | RouletteRank int64 `json:"rouletteRank"` 23 | NumSpecialEgg int64 `json:"numSpecialEgg"` 24 | NumRouletteToken int64 `json:"numRouletteToken"` 25 | NumJackpotRing int64 `json:"numJackpotRing"` 26 | NumRemainingRoulette int64 `json:"numRemainingRoulette"` 27 | SpinID int64 `json:"spinID"` 28 | CostItemList []obj.CostItem `json:"costItemList"` 29 | } 30 | 31 | func DefaultRaidbossWheelOptions(numRouletteTicket, numSpecialEgg, rouletteCountInPeriod, rouletteRank, freeSpins int64) RaidbossWheelOptions { 32 | items := []string{strconv.Itoa(enums.IDTypeItemRouletteWin)} // first item is always jackpot/big/super 33 | item := []int64{1} 34 | itemWeight := []int64{1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250} 35 | chaoIDs, err := roulette.GetRandomChaoWheelChao(2, 7) 36 | if err != nil { 37 | log.Printf("error generating chao list: %s\n", err.Error()) 38 | } 39 | if config.CFile.Debug { 40 | for _ = range make([]byte, 7) { // loop 7 times 41 | items = append(items, chaoIDs[len(items)-1]) 42 | item = append(item, 1) 43 | } 44 | } else { 45 | for _ = range make([]byte, 7) { // loop 7 times 46 | items = append(items, strconv.Itoa(enums.ItemIDRing)) 47 | item = append(item, 1000) 48 | } 49 | } 50 | itemWon := int64(rand.Intn(len(items))) //TODO: adjust this to accurately represent item weights 51 | nextFreeSpin := now.EndOfDay().UTC().Unix() + 1 // midnight 52 | numJackpotRing := int64(consts.RouletteJackpotRings) 53 | // TODO: get rid of logic here! 54 | costItemList := []obj.CostItem{ 55 | obj.NewCostItem( 56 | strconv.Itoa(enums.ItemIDRaidbossRing), 57 | 5000, 58 | 27272, 59 | ), 60 | } 61 | numRouletteToken := numRouletteTicket 62 | if numRouletteToken > 0 { 63 | costItemList[0] = obj.NewCostItem( 64 | strconv.Itoa(enums.IDRouletteTicketRaid), 65 | 1, 66 | numRouletteToken, 67 | ) 68 | } 69 | numRemainingRoulette := numRouletteToken + freeSpins - rouletteCountInPeriod // TODO: is this proper? 70 | if numRemainingRoulette < numRouletteToken { 71 | numRemainingRoulette = numRouletteToken 72 | } 73 | spinID := int64(0) 74 | out := RaidbossWheelOptions{ 75 | items, 76 | item, 77 | itemWeight, 78 | itemWon, 79 | nextFreeSpin, 80 | rouletteRank, 81 | numSpecialEgg, 82 | numRouletteToken, 83 | numJackpotRing, 84 | numRemainingRoulette, 85 | spinID, 86 | costItemList, 87 | } 88 | return out 89 | } 90 | -------------------------------------------------------------------------------- /netobj/rouletteInfo.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import "github.com/jinzhu/now" 4 | 5 | type RouletteInfo struct { 6 | RoulettePeriodEnd int64 `json:"ORN_roulettePeriodEnd"` 7 | RouletteCountInPeriod int64 `json:"ORN_rouletteCountInPeriod"` 8 | GotJackpotThisPeriod bool `json:"ORN_gotJackpotThisPeriod"` 9 | } 10 | 11 | func DefaultRouletteInfo() RouletteInfo { 12 | roulettePeriodEnd := now.EndOfDay().Unix() 13 | rouletteCountInPeriod := int64(0) 14 | gotJackpotThisPeriod := false 15 | return RouletteInfo{ 16 | roulettePeriodEnd, 17 | rouletteCountInPeriod, 18 | gotJackpotThisPeriod, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /netobj/transferCredentials.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | type TransferCredentials struct { 4 | TransferID string `json:"transferId"` 5 | TransferPassword string `json:"transferPassword"` 6 | PlayerID string `json:"playerId"` 7 | } 8 | 9 | func DefaultTransferCredentials() TransferCredentials { 10 | return TransferCredentials{ 11 | "", 12 | "", 13 | "0000000000", 14 | } 15 | } 16 | 17 | func PlayerToTransferCredentials(player Player) TransferCredentials { 18 | return TransferCredentials{ 19 | player.MigrationPassword, 20 | player.UserPassword, 21 | player.ID, 22 | } 23 | } 24 | 25 | func VerifyTransferPasswordAndGetPlayerID(transferCreds TransferCredentials, userPassword string) (bool, string) { 26 | if transferCreds.TransferPassword == userPassword { 27 | return true, transferCreds.PlayerID 28 | } else { 29 | return false, "" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /netobj/userRaidBossState.go: -------------------------------------------------------------------------------- 1 | package netobj 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type EventUserRaidbossState struct { 8 | NumRaidbossRings int64 `json:"numRaidbossRings"` // NOTE: these are different from normal rings! 9 | RaidBossEnergy int64 `json:"raidbossEnergy"` 10 | RaidBossEnergyBuy int64 `json:"raidbossEnergyBuy"` 11 | NumBeatedEncounter int64 `json:"numBeatedEncounter"` // number of times the boss has been defeated? 12 | NumBeatedEnterprise int64 `json:"numBeatedEnterprise"` // ? 13 | NumRaidBossEncountered int64 `json:"numTotalEncountered"` 14 | EnergyRenewsAt int64 `json:"raidbossEnergyRenewsAt"` 15 | } 16 | 17 | func DefaultUserRaidbossState() EventUserRaidbossState { 18 | // TODO: establish as constants 19 | numRaidbossRings := int64(0) 20 | raidbossEnergy := int64(20) 21 | raidbossEnergyBuy := int64(0) 22 | numBeatedEncounter := int64(0) 23 | numBeatedEnterprise := int64(0) 24 | numRaidBossEncountered := int64(0) 25 | energyRenewsAt := int64(time.Now().UTC().Unix() + 600) // in ten minutes 26 | return EventUserRaidbossState{ 27 | numRaidbossRings, 28 | raidbossEnergy, 29 | raidbossEnergyBuy, 30 | numBeatedEncounter, 31 | numBeatedEnterprise, 32 | numRaidBossEncountered, 33 | energyRenewsAt, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /obj/battleData.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/RunnersRevival/outrun/enums" 7 | ) 8 | 9 | type BattleData struct { 10 | UserID string `json:"userId"` // user ID 11 | Name string `json:"name"` // username 12 | MaxScore int64 `json:"maxScore"` // highest score gained 13 | League int64 `json:"league"` // enums.RankingLeague* 14 | LoginTime int64 `json:"loginTime"` // used by game in ranking window with GetCurrentTime() - loginTime 15 | MainChaoID string `json:"mainChaoId"` // TODO: if this struct is a problem for the game, the game actually only expects the first two strings to be real strings; these are ints 16 | MainChaoLevel int64 `json:"mainChaoLevel"` 17 | SubChaoID string `json:"subChaoId"` 18 | SubChaoLevel int64 `json:"subChaoLevel"` 19 | Rank int64 `json:"numRank"` // player rank 20 | MainCharaID string `json:"charaId"` 21 | MainCharaLevel int64 `json:"charaLevel"` 22 | SubCharaID string `json:"subCharaId"` 23 | SubCharaLevel int64 `json:"subCharaLevel"` 24 | GoOnWin int64 `json:"goOnWin"` // win streak 25 | IsSentEnergy int64 `json:"energyFlg"` // you can't send energy to friend if true (is only friend if social connection); enables custom icon 26 | Language int64 `json:"language"` // enums.Lang* 27 | } 28 | 29 | func DebugRivalBattleData() BattleData { 30 | return BattleData{ 31 | "4204206969", 32 | "MORBIUS", 33 | 1020304, 34 | enums.RankingLeagueS, 35 | time.Now().UTC().Unix() - 60000, 36 | "400009", 37 | 4, 38 | "400000", 39 | 9, 40 | 2, 41 | enums.CTStrGothicAmy, // TODO: test if this will take event characters...? 42 | 12, 43 | enums.CTStrTails, 44 | 34, 45 | 79, 46 | 0, 47 | enums.LangEnglish, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /obj/battlePair.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import "sort" 4 | 5 | type BattlePair struct { // This is just used for organization within the response 6 | StartTime int64 `json:"startTime"` 7 | EndTime int64 `json:"endTime"` 8 | BattleData BattleData `json:"battleData"` 9 | RivalBattleData BattleData `json:"rivalBattleData"` 10 | } 11 | 12 | func NewBattlePair(startTime, endTime int64, battleData, rivalBattleData BattleData) BattlePair { 13 | return BattlePair{ 14 | startTime, 15 | endTime, 16 | battleData, 17 | rivalBattleData, 18 | } 19 | } 20 | 21 | type RewardBattlePair struct { // This is just used for organization within the response 22 | StartTime int64 `json:"rewardStartTime"` 23 | EndTime int64 `json:"rewardEndTime"` 24 | BattleData BattleData `json:"rewardBattleData"` 25 | RivalBattleData BattleData `json:"rewardRivalBattleData"` 26 | } 27 | 28 | func NewRewardBattlePair(startTime, endTime int64, battleData, rivalBattleData BattleData) RewardBattlePair { 29 | return RewardBattlePair{ 30 | startTime, 31 | endTime, 32 | battleData, 33 | rivalBattleData, 34 | } 35 | } 36 | 37 | // The game expects the battle pairs to be sorted by newest first. If it isn't, "Did not join" entries show up when they're not supposed to. 38 | func SortBattlePairsByNewestFirst(pairs []BattlePair) []BattlePair { 39 | sort.Slice(pairs, func(i, j int) bool { 40 | return (int)(pairs[i].EndTime-pairs[i].StartTime) > (int)(pairs[j].EndTime-pairs[i].StartTime) 41 | }) 42 | return pairs 43 | } 44 | 45 | func BattlePairsAreSorted(pairs []BattlePair) bool { 46 | return sort.SliceIsSorted(pairs, func(i, j int) bool { 47 | return (int)(pairs[i].EndTime-pairs[i].StartTime) > (int)(pairs[j].EndTime-pairs[i].StartTime) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /obj/battleStatus.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type BattleStatus struct { 4 | Wins int64 `json:"numWin"` // battle wins 5 | Losses int64 `json:"numLose"` // battle losses 6 | Draws int64 `json:"numDraw"` // battle draws 7 | LossesByDefault int64 `json:"numLoseByDefault"` // battle failures 8 | GoOnWins int64 `json:"goOnWin"` // battle win streak 9 | GoOnLosses int64 `json:"goOnLosses"` // battle loss streak (possibly used by matchmaking? it isn't shown by the client) 10 | } 11 | 12 | func DefaultBattleStatus() BattleStatus { 13 | return BattleStatus{ 14 | 0, 15 | 0, 16 | 0, 17 | 0, 18 | 0, 19 | 0, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /obj/campaign.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import "time" 4 | 5 | type Campaign struct { 6 | Type int64 `json:"campaignType"` 7 | Content int64 `json:"campaignContent"` 8 | SubContent int64 `json:"campaignSubContent"` 9 | StartTime int64 `json:"campaignStartTime"` 10 | EndTime int64 `json:"campaignEndTime"` 11 | } 12 | 13 | func NewCampaign(ctype, content, subcontent, startTime, endTime int64) Campaign { 14 | return Campaign{ 15 | ctype, 16 | content, 17 | subcontent, 18 | startTime, 19 | endTime, 20 | } 21 | } 22 | 23 | func DefaultCampaign(ctype, content, subcontent int64) Campaign { 24 | return NewCampaign( 25 | ctype, 26 | content, 27 | subcontent, 28 | 1565499998, 29 | 1565499999, 30 | ) 31 | } 32 | 33 | func IsCampaignActive(campaign Campaign) bool { 34 | return time.Now().UTC().Unix() >= campaign.StartTime && time.Now().UTC().Unix() < campaign.EndTime 35 | } 36 | -------------------------------------------------------------------------------- /obj/chao.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Chao struct { 4 | ID string `json:"chaoId"` 5 | Rarity int64 `json:"rarity"` 6 | Hidden int64 `json:"hidden"` // this value is required, but is never really used in game... Best to keep at "1" 7 | } 8 | 9 | func NewChao(id string, rarity, hidden int64) Chao { 10 | return Chao{ 11 | id, 12 | rarity, 13 | hidden, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /obj/chaoPrize.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type ChaoPrize struct { 4 | ID string `json:"chao_id"` 5 | Rarity int64 `json:"rarity,string"` 6 | } 7 | 8 | func NewChaoPrize(id string, rarity int64) ChaoPrize { 9 | return ChaoPrize{ 10 | id, 11 | rarity, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /obj/character.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Character struct { 4 | ID string `json:"characterId"` 5 | Cost int64 `json:"numRings"` // interestingly, is used for both buying the character and for levelling up... 6 | NumRedRings int64 `json:"numRedRings"` // ? 7 | Price int64 `json:"priceNumRings"` // used to limit break, as far as I can tell? 8 | PriceRedRings int64 `json:"priceNumRedRings"` // ? 9 | } 10 | -------------------------------------------------------------------------------- /obj/constobjs/chao.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/obj" 7 | ) 8 | 9 | var ChaoIDs = []string{"400000", "400001", "400002", "400003", "400004", "400005", "400006", "400007", "400008", "400009", "400010", "400011", "400012", "400013", "400014", "400015", "400016", "400017", "400018", "400019", "400020", "400021", "400022", "400023", "400024", "400025", "401000", "401001", "401002", "401003", "401004", "401005", "401006", "401007", "401008", "401009", "401010", "401011", "401012", "401013", "401014", "401015", "401016", "401017", "401018", "401019", "401020", "401021", "401022", "401023", "401024", "401025", "401026", "401027", "401028", "401029", "401030", "401031", "401032", "401033", "401034", "401035", "401036", "401037", "401038", "401039", "401040", "401041", "401042", "401043", "401044", "401045", "401046", "401047", "402000", "402001", "402002", "402003", "402004", "402005", "402006", "402007", "402008", "402009", "402010", "402011", "402012", "402013", "402014", "402015", "402016", "402017", "402018", "402019", "402020", "402021", "402022", "402023", "402024", "402025", "402026", "402027", "402028", "402029", "402030", "402031", "402032", "402033", "402034"} 10 | 11 | var Chao = GetAllChao() 12 | 13 | func GetAllChao() map[string]obj.Chao { 14 | chaos := make(map[string]obj.Chao) 15 | for _, chaoID := range ChaoIDs { 16 | id := chaoID 17 | rarity, _ := strconv.Atoi(string(id[2])) // numerical rarity (second digit) 18 | hidden := int64(0) // TODO: discover what this is used for (see obj/chao.go) 19 | chao := obj.Chao{ 20 | id, 21 | int64(rarity), 22 | hidden, 23 | } 24 | chaos[chaoID] = chao 25 | } 26 | return chaos 27 | } 28 | -------------------------------------------------------------------------------- /obj/constobjs/defaultChaoPrizeWheelPrizeList.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | ) 6 | 7 | var DefaultChaoPrizeWheelPrizeList = []obj.ChaoPrize{ 8 | obj.NewChaoPrize("401000", 1), 9 | } 10 | 11 | var DefaultChaoPrizeWheelPrizeListOLD = []obj.ChaoPrize{ 12 | obj.NewChaoPrize("300000", 100), 13 | obj.NewChaoPrize("300001", 100), 14 | obj.NewChaoPrize("300002", 100), 15 | obj.NewChaoPrize("401000", 1), 16 | obj.NewChaoPrize("401001", 1), 17 | obj.NewChaoPrize("401002", 1), 18 | obj.NewChaoPrize("401010", 1), 19 | obj.NewChaoPrize("401015", 1), 20 | obj.NewChaoPrize("401005", 1), 21 | obj.NewChaoPrize("401003", 1), 22 | obj.NewChaoPrize("401004", 1), 23 | obj.NewChaoPrize("401022", 1), 24 | obj.NewChaoPrize("401016", 1), 25 | obj.NewChaoPrize("401029", 1), 26 | obj.NewChaoPrize("401020", 1), 27 | obj.NewChaoPrize("401031", 1), 28 | obj.NewChaoPrize("401033", 1), 29 | obj.NewChaoPrize("401026", 1), 30 | obj.NewChaoPrize("401027", 1), 31 | obj.NewChaoPrize("401009", 1), 32 | obj.NewChaoPrize("401017", 1), 33 | obj.NewChaoPrize("401030", 1), 34 | obj.NewChaoPrize("401038", 1), 35 | obj.NewChaoPrize("401037", 1), 36 | obj.NewChaoPrize("401034", 1), 37 | obj.NewChaoPrize("401035", 1), 38 | obj.NewChaoPrize("401036", 1), 39 | obj.NewChaoPrize("401028", 1), 40 | obj.NewChaoPrize("402001", 2), 41 | obj.NewChaoPrize("402003", 2), 42 | obj.NewChaoPrize("402005", 2), 43 | obj.NewChaoPrize("402008", 2), 44 | obj.NewChaoPrize("402015", 2), 45 | obj.NewChaoPrize("402007", 2), 46 | obj.NewChaoPrize("402021", 2), 47 | obj.NewChaoPrize("402014", 2), 48 | obj.NewChaoPrize("402024", 2), 49 | obj.NewChaoPrize("402000", 2), 50 | obj.NewChaoPrize("402002", 2), 51 | obj.NewChaoPrize("402020", 2), 52 | obj.NewChaoPrize("402016", 2), 53 | obj.NewChaoPrize("402017", 2), 54 | obj.NewChaoPrize("402018", 2), 55 | obj.NewChaoPrize("402006", 2), 56 | obj.NewChaoPrize("300005", 100), 57 | obj.NewChaoPrize("300014", 100), 58 | obj.NewChaoPrize("300015", 100), 59 | obj.NewChaoPrize("300018", 100), 60 | obj.NewChaoPrize("300020", 100), 61 | obj.NewChaoPrize("300016", 100), 62 | obj.NewChaoPrize("300017", 100), 63 | obj.NewChaoPrize("300019", 100), 64 | obj.NewChaoPrize("401011", 1), 65 | obj.NewChaoPrize("401023", 1), 66 | obj.NewChaoPrize("401024", 1), 67 | obj.NewChaoPrize("401025", 1), 68 | obj.NewChaoPrize("401046", 1), 69 | obj.NewChaoPrize("401047", 1), 70 | obj.NewChaoPrize("401040", 1), 71 | obj.NewChaoPrize("401041", 1), 72 | obj.NewChaoPrize("402034", 2), 73 | obj.NewChaoPrize("402030", 2), 74 | } 75 | -------------------------------------------------------------------------------- /obj/constobjs/defaultCostListConsumedItem.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import "github.com/RunnersRevival/outrun/obj" 4 | 5 | var DefaultCostList = getDefaultCostList() 6 | 7 | func getDefaultCostList() []obj.ConsumedItem { 8 | result := []obj.ConsumedItem{} 9 | cids1 := []string{"110000", "110001", "110002"} 10 | cids1IDs := []string{"910000", "910000", "910000"} 11 | cids1Num := []int64{6000, 1000, 4000} 12 | cids2 := []string{"120000", "120001", "120002", "120003", "120004", "120005", "120006", "120007", "120008", "120009", "120010"} 13 | cids2IDs := []string{"910000", "910000", "910000", "910000", "910000", "910000", "910000", "910000", "910000", "910000", "910000"} 14 | cids2Num := []int64{3000, 1000, 3000, 2000, 3000, 5000, 4000, 5000, 0, 0, 0} 15 | cids3 := []string{"950000", "980000", "980001", "980002"} 16 | cids3IDs := []string{"900000", "900000", "900000", "900000"} 17 | cids3Num := []int64{3, 2, 5, 40} // TODO: move first entry to consts 18 | // cids3: Continue cost, unknown (possibly unused?) daily battle cost, normal reroll cost, stricter reroll cost 19 | cids := []string{} 20 | cids = append(cids, cids1...) 21 | cids = append(cids, cids2...) 22 | cids = append(cids, cids3...) 23 | cidsIDs := []string{} 24 | cidsIDs = append(cidsIDs, cids1IDs...) 25 | cidsIDs = append(cidsIDs, cids2IDs...) 26 | cidsIDs = append(cidsIDs, cids3IDs...) 27 | cidsNum := []int64{} 28 | cidsNum = append(cidsNum, cids1Num...) 29 | cidsNum = append(cidsNum, cids2Num...) 30 | cidsNum = append(cidsNum, cids3Num...) 31 | for i, cid := range cids { 32 | id := cidsIDs[i] 33 | num := cidsNum[i] 34 | iinfo := obj.NewItem( 35 | id, 36 | num, 37 | ) 38 | ci := obj.ConsumedItem{ 39 | iinfo, 40 | cid, 41 | } 42 | result = append(result, ci) 43 | } 44 | return result 45 | } 46 | -------------------------------------------------------------------------------- /obj/constobjs/defaultFirstLoginBonusRewardList.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import "github.com/RunnersRevival/outrun/obj" 4 | 5 | var DefaultFirstLoginBonusRewardList = func() []obj.LoginBonusReward { 6 | return []obj.LoginBonusReward{ 7 | obj.NewLoginBonusReward( 8 | []obj.SelectReward{ 9 | obj.NewSelectReward( 10 | []obj.Item{ 11 | obj.NewItem("900000", 10), 12 | obj.NewItem("910000", 10000), 13 | obj.NewItem("240000", 3), 14 | }, 15 | ), 16 | }, 17 | ), 18 | obj.NewLoginBonusReward( 19 | []obj.SelectReward{ 20 | obj.NewSelectReward( 21 | []obj.Item{ 22 | obj.NewItem("900000", 20), 23 | obj.NewItem("910000", 20000), 24 | obj.NewItem("240000", 3), 25 | }, 26 | ), 27 | }, 28 | ), 29 | obj.NewLoginBonusReward( 30 | []obj.SelectReward{ 31 | obj.NewSelectReward( 32 | []obj.Item{ 33 | obj.NewItem("900000", 20), 34 | obj.NewItem("910000", 30000), 35 | obj.NewItem("240000", 3), 36 | }, 37 | ), 38 | }, 39 | ), 40 | obj.NewLoginBonusReward( 41 | []obj.SelectReward{ 42 | obj.NewSelectReward( 43 | []obj.Item{ 44 | obj.NewItem("230000", 1), 45 | obj.NewItem("910000", 50000), 46 | obj.NewItem("240000", 3), 47 | }, 48 | ), 49 | }, 50 | ), 51 | obj.NewLoginBonusReward( 52 | []obj.SelectReward{ 53 | obj.NewSelectReward( 54 | []obj.Item{ 55 | obj.NewItem("300013", 1), 56 | obj.NewItem("910000", 50000), 57 | obj.NewItem("240000", 3), 58 | }, 59 | ), 60 | }, 61 | ), 62 | } 63 | }() 64 | -------------------------------------------------------------------------------- /obj/constobjs/defaultLeagueData.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import "github.com/RunnersRevival/outrun/obj" 4 | 5 | var DefaultLeagueDataMode0 = obj.NewLeagueData( 6 | 0, 7 | 0, 8 | 40, 9 | 0, 10 | 0, 11 | []obj.OperatorScore{ 12 | obj.NewOperatorScore(2, 50, []obj.Item{}), 13 | obj.NewOperatorScore(2, 40, []obj.Item{obj.NewItem("910000", 3000)}), 14 | obj.NewOperatorScore(2, 30, []obj.Item{obj.NewItem("910000", 5000)}), 15 | obj.NewOperatorScore(2, 20, []obj.Item{obj.NewItem("910000", 7000)}), 16 | obj.NewOperatorScore(2, 10, []obj.Item{obj.NewItem("910000", 10000)}), 17 | obj.NewOperatorScore(2, 1, []obj.Item{obj.NewItem("910000", 15000)}), 18 | obj.NewOperatorScore(0, 1, []obj.Item{obj.NewItem("910000", 20000)}), 19 | }, 20 | []obj.OperatorScore{ 21 | obj.NewOperatorScore(2, 50, []obj.Item{}), 22 | obj.NewOperatorScore(2, 40, []obj.Item{obj.NewItem("910000", 3000)}), 23 | obj.NewOperatorScore(2, 30, []obj.Item{obj.NewItem("910000", 5000)}), 24 | obj.NewOperatorScore(2, 20, []obj.Item{obj.NewItem("910000", 7000)}), 25 | obj.NewOperatorScore(2, 10, []obj.Item{obj.NewItem("910000", 10000)}), 26 | obj.NewOperatorScore(2, 1, []obj.Item{obj.NewItem("910000", 15000)}), 27 | obj.NewOperatorScore(0, 1, []obj.Item{obj.NewItem("910000", 20000)}), 28 | }, 29 | ) 30 | 31 | var DefaultLeagueDataMode1 = obj.NewLeagueData( 32 | 0, 33 | 0, 34 | 40, 35 | 0, 36 | 0, 37 | []obj.OperatorScore{ 38 | obj.NewOperatorScore(2, 50, []obj.Item{}), 39 | obj.NewOperatorScore(2, 40, []obj.Item{obj.NewItem("900000", 5)}), 40 | obj.NewOperatorScore(2, 30, []obj.Item{obj.NewItem("900000", 10)}), 41 | obj.NewOperatorScore(2, 20, []obj.Item{obj.NewItem("900000", 15)}), 42 | obj.NewOperatorScore(2, 10, []obj.Item{obj.NewItem("900000", 15)}), 43 | obj.NewOperatorScore(2, 1, []obj.Item{obj.NewItem("900000", 20)}), 44 | obj.NewOperatorScore(0, 1, []obj.Item{obj.NewItem("900000", 20)}), 45 | }, 46 | []obj.OperatorScore{ 47 | obj.NewOperatorScore(2, 50, []obj.Item{}), 48 | obj.NewOperatorScore(2, 40, []obj.Item{obj.NewItem("900000", 3)}), 49 | obj.NewOperatorScore(2, 30, []obj.Item{obj.NewItem("900000", 5)}), 50 | obj.NewOperatorScore(2, 20, []obj.Item{obj.NewItem("900000", 5)}), 51 | obj.NewOperatorScore(2, 10, []obj.Item{obj.NewItem("900000", 10)}), 52 | obj.NewOperatorScore(2, 1, []obj.Item{obj.NewItem("900000", 10)}), 53 | obj.NewOperatorScore(0, 1, []obj.Item{obj.NewItem("900000", 10)}), 54 | }, 55 | ) 56 | -------------------------------------------------------------------------------- /obj/constobjs/defaultMessages.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | ) 6 | 7 | var DefaultInformations = []obj.Information{ 8 | obj.NewInformation(531, 1, 1465808400, 1609405749, "3__10500013_14"), 9 | //obj.NewInformation(6001070, 1, 1464336180, 1580608922, "2_[ffff00]'You feel it too, don't you?'[ffffff]\nWelcome to [ff0000]OUTRUN[ffffff], a custom Sonic Runners server!\n\n(Last local Git update: "+getLastGitCommitDate()+")\r\n_10600001_0"), 10 | obj.NewInformation(6001070, 1, 1464336180, 1609405749, "2_[ffff00]Welcome to the updated Runners Revival. Now powered by Outrun.\r\n_10600001_0"), 11 | //obj.NewInformation(1000230, 3, 1465981200, 1466413199, "1__90000001_1"), 12 | //obj.NewInformation(1000157, 600, 1448614800, 1609459199, "1__90000002_1"), 13 | } 14 | -------------------------------------------------------------------------------- /obj/constobjs/defaultMileageIncentives.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | ) 6 | 7 | func GetMileageIncentives(episode, chapter, point int64) []obj.MileageIncentive { 8 | rewards := GetAreaReward(chapter, episode) 9 | // convert rewards to mileageIncentives 10 | // TODO: probably not the best to do this at runtime... 11 | incentives := []obj.MileageIncentive{} 12 | for _, reward := range rewards { 13 | var incentive obj.MileageIncentive 14 | incentive.Type = reward.Type 15 | incentive.ItemID = reward.ItemID 16 | // TODO: incentive.FriendID should likely be satisfied at some point 17 | incentive.NumItem = reward.NumItem 18 | incentive.PointID = reward.Point 19 | if point <= reward.Point { 20 | incentives = append(incentives, incentive) 21 | } 22 | } 23 | return incentives 24 | } 25 | -------------------------------------------------------------------------------- /obj/constobjs/defaultPlayerStateItems.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/enums" 7 | "github.com/RunnersRevival/outrun/obj" 8 | ) 9 | 10 | var DefaultPlayerStateItems = func() []obj.Item { 11 | items := []obj.Item{} 12 | baseNum := 120000 13 | for i := range make([]byte, 11) { // 0...10 14 | n := baseNum + i 15 | s := strconv.Itoa(n) 16 | item := obj.NewItem(s, 0) 17 | if int64(n) == enums.ItemIDLaser { // free item at the start of the game 18 | item.Amount = 1 19 | } 20 | items = append(items, item) 21 | } 22 | return items 23 | }() 24 | -------------------------------------------------------------------------------- /obj/constobjs/defaultRedStarItems.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | ) 6 | 7 | var RedStarItemsType1 = rsiDefaultsIT1() // Rings 8 | var RedStarItemsType2 = rsiDefaultsIT2() // Energies 9 | var RedStarItemsType4 = rsiDefaultsIT4() // Red Rings?? See ShopUI.ServerEexchangeType 10 | 11 | func rsiDefaultsIT1() []obj.RedStarItem { 12 | redstaritems := []obj.RedStarItem{} 13 | storeItemIds := []string{"915001", "915002", "915003", "915004", "915005"} 14 | itemIds := []string{"910000", "910000", "910000", "910000", "910000"} 15 | numItems := []int64{20000, 42000, 108000, 224000, 600000} 16 | prices := []int64{10, 20, 50, 100, 250} 17 | priceDisps := []string{"", "", "", "", ""} 18 | productIds := []string{"", "", "", "", ""} 19 | for i, storeItemId := range storeItemIds { 20 | itemId := itemIds[i] 21 | numItem := numItems[i] 22 | price := prices[i] 23 | priceDisp := priceDisps[i] 24 | productId := productIds[i] 25 | rsi := obj.NewRedStarItem( 26 | storeItemId, 27 | itemId, 28 | priceDisp, 29 | productId, 30 | numItem, 31 | price, 32 | nil, 33 | ) 34 | redstaritems = append(redstaritems, rsi) 35 | } 36 | return redstaritems 37 | } 38 | 39 | func rsiDefaultsIT2() []obj.RedStarItem { 40 | redstaritems := []obj.RedStarItem{} 41 | storeItemIds := []string{"920005", "920020", "920030", "920050", "920100"} 42 | itemIds := []string{"920000", "920000", "920000", "920000", "920000"} 43 | numItems := []int64{5, 20, 30, 50, 100} 44 | prices := []int64{10, 35, 50, 80, 150} 45 | priceDisps := []string{"", "", "", "", ""} 46 | productIds := []string{"", "", "", "", ""} 47 | for i, storeItemId := range storeItemIds { 48 | itemId := itemIds[i] 49 | numItem := numItems[i] 50 | price := prices[i] 51 | priceDisp := priceDisps[i] 52 | productId := productIds[i] 53 | campaign := obj.DefaultCampaign(8, 2000, 0) // WARN: is this a possibility for a memory leak? 54 | rsi := obj.NewRedStarItem( 55 | storeItemId, 56 | itemId, 57 | priceDisp, 58 | productId, 59 | numItem, 60 | price, 61 | &campaign, 62 | ) 63 | redstaritems = append(redstaritems, rsi) 64 | } 65 | return redstaritems 66 | } 67 | 68 | func rsiDefaultsIT4() []obj.RedStarItem { 69 | redstaritems := []obj.RedStarItem{} 70 | storeItemIds := []string{"940005", "940020", "940030", "940050", "940100"} 71 | itemIds := []string{"940000", "940000", "940000", "940000", "940000"} 72 | numItems := []int64{5, 20, 30, 50, 100} 73 | prices := []int64{10, 35, 50, 80, 150} 74 | priceDisps := []string{"", "", "", "", ""} 75 | productIds := []string{"", "", "", "", ""} 76 | for i, storeItemId := range storeItemIds { 77 | itemId := itemIds[i] 78 | numItem := numItems[i] 79 | price := prices[i] 80 | priceDisp := priceDisps[i] 81 | productId := productIds[i] 82 | rsi := obj.NewRedStarItem( 83 | storeItemId, 84 | itemId, 85 | priceDisp, 86 | productId, 87 | numItem, 88 | price, 89 | nil, 90 | ) 91 | redstaritems = append(redstaritems, rsi) 92 | } 93 | return redstaritems 94 | } 95 | -------------------------------------------------------------------------------- /obj/constobjs/defaultSpinItems.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/enums" 7 | "github.com/RunnersRevival/outrun/obj" 8 | ) 9 | 10 | var DefaultSpinItems = []obj.Item{ 11 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 1), 12 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 2), 13 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 3), 14 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 4), 15 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 5), 16 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 6), 17 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 7), 18 | obj.NewItem(strconv.Itoa(int(enums.ItemIDAsteroid)), 8), 19 | } 20 | -------------------------------------------------------------------------------- /obj/constobjs/items.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | import "github.com/RunnersRevival/outrun/obj" 4 | 5 | var ItemIDs = []string{"110000", "110001", "110002", "120000", "120001", "120002", "120003", "120004", "120005", "120006", "120007", "120008", "120009", "120010"} 6 | var AllItems = getAllItems() 7 | 8 | func getAllItems() []obj.Item { 9 | items := []obj.Item{} 10 | for _, iid := range ItemIDs { 11 | item := obj.NewItem(iid, 1) 12 | items = append(items, item) 13 | } 14 | return items 15 | } 16 | -------------------------------------------------------------------------------- /obj/constobjs/shopAmounts.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | var ( 4 | ShopRingAmounts = map[string]int64{ 5 | "915001": 20000, 6 | "915002": 42000, 7 | "915003": 108000, 8 | "915004": 224000, 9 | "915005": 600000, 10 | } 11 | ShopEnergyAmounts = map[string]int64{ 12 | "920005": 5, 13 | "920020": 20, 14 | "920030": 30, 15 | "920050": 50, 16 | "920100": 100, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /obj/constobjs/shopPrices.go: -------------------------------------------------------------------------------- 1 | package constobjs 2 | 3 | var ( 4 | ShopRingPrices = map[string]int64{ 5 | "915001": 10, 6 | "915002": 20, 7 | "915003": 50, 8 | "915004": 100, 9 | "915005": 250, 10 | } 11 | ShopEnergyPrices = map[string]int64{ 12 | "920005": 10, 13 | "920020": 35, 14 | "920030": 50, 15 | "920050": 80, 16 | "920100": 150, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /obj/consumedItem.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type ConsumedItem struct { 4 | Item 5 | ID string `json:"consumedItemId"` 6 | } 7 | 8 | func NewConsumedItem(item Item, id string) ConsumedItem { 9 | return ConsumedItem{ 10 | item, 11 | id, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /obj/costItem.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type CostItem struct { 4 | ID string `json:"itemId"` 5 | Amount int64 `json:"numItem,string"` 6 | ItemStock int64 `json:"itemStock"` 7 | } 8 | 9 | func NewCostItem(iid string, amount, stock int64) CostItem { 10 | return CostItem{ 11 | iid, 12 | amount, 13 | stock, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /obj/event.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Event struct { 4 | ID int64 `json:"eventId"` // used to get the type... For some reason... 5 | Type int64 `json:"eventType"` // Dead code in game but still must be satisfied 6 | StartTime int64 `json:"eventStartTime"` // UTC time 7 | EndTime int64 `json:"eventEndTime"` // UTC time 8 | CloseTime int64 `json:"eventCloseTime"` // UTC time 9 | } 10 | 11 | func NewEvent(id int64, eventType uint, startTime, endTime, closeTime int64) Event { 12 | return Event{ 13 | id, 14 | int64(eventType), 15 | startTime, 16 | endTime, 17 | closeTime, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /obj/eventReward.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type EventReward struct { 4 | Item 5 | RewardID int64 `json:"rewardId"` 6 | Param int64 `json:"param"` 7 | } 8 | 9 | func NewEventReward(rewardId, param int64, itemId string, numItem int64) EventReward { 10 | return EventReward{ 11 | NewItem(itemId, numItem), 12 | rewardId, 13 | param, 14 | } 15 | } 16 | 17 | func ItemToEventReward(i Item, rid int64, param int64) EventReward { 18 | return EventReward{ 19 | i, 20 | rid, 21 | param, 22 | } 23 | } -------------------------------------------------------------------------------- /obj/incentive.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Incentive struct { 4 | Item 5 | IncentiveCount int64 `json:"numIncentiveCont"` 6 | } 7 | 8 | func NewIncentive(item Item, count int64) Incentive { 9 | return Incentive{ 10 | item, 11 | count, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /obj/information.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | // TODO: create a param object that makes it easier to create informations 4 | 5 | type Information struct { 6 | ID int64 `json:"id"` 7 | Priority int64 `json:"priority"` 8 | Start int64 `json:"start"` 9 | End int64 `json:"end"` 10 | Param string `json:"param"` 11 | } 12 | 13 | func NewInformation(id, priority, start, end int64, param string) Information { 14 | return Information{ 15 | id, 16 | priority, 17 | start, 18 | end, 19 | param, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /obj/item.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Item struct { 4 | ID string `json:"itemId"` 5 | Amount int64 `json:"numItem,string"` 6 | } 7 | 8 | func NewItem(iid string, amount int64) Item { 9 | return Item{ 10 | iid, 11 | amount, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /obj/leaderboardEntry.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | ) 7 | 8 | type LeaderboardEntry struct { 9 | FriendID string `json:"friendId"` 10 | Name string `json:"name"` 11 | URL string `json:"url"` 12 | Grade int64 `json:"grade"` 13 | ExposeOnline int64 `json:"exposeOnline"` // internal use on the OG server - game does not reference this field 14 | RankingScore int64 `json:"rankingScore"` 15 | RankChanged int64 `json:"rankChanged"` // internal use on the OG server - game does not reference this field 16 | IsSentEnergy int64 `json:"energyFlg"` 17 | ExpireTime int64 `json:"expireTime"` // TODO: doesn't seem to be used 18 | NumRank int64 `json:"numRank"` // left adjusted with '0's to match length 3; unknown of use, called internally 'mapRank' 19 | LoginTime int64 `json:"loginTime"` 20 | CharacterID string `json:"charaId"` 21 | CharacterLevel int64 `json:"characterLevel"` 22 | SubcharacterID string `json:"subCharaId"` 23 | SubcharacterLevel int64 `json:"subCharaLevel"` 24 | MainChaoID string `json:"mainChaoId"` 25 | MainChaoLevel int64 `json:"mainChaoLevel"` 26 | SubChaoID string `json:"subChaoId"` 27 | SubChaoLevel int64 `json:"subChaoLevel"` 28 | Language int64 `json:"language"` // enums.Lang* 29 | League int64 `json:"league"` 30 | MaxScore int64 `json:"maxScore"` 31 | } 32 | 33 | func NewLeaderboardEntry(fid, n, url string, g, eo, rs, rc, ise, et, nr, lt, cid, cl, schid, schl, mcid, mcl, scid, scl, lang, league, maxScore int64) LeaderboardEntry { 34 | return LeaderboardEntry{ 35 | fid, 36 | n, 37 | url, 38 | g, 39 | eo, 40 | rs, 41 | rc, 42 | ise, 43 | et, 44 | nr, 45 | lt, 46 | strconv.Itoa(int(cid)), 47 | cl, 48 | strconv.Itoa(int(schid)), 49 | schl, 50 | strconv.Itoa(int(mcid)), 51 | mcl, 52 | strconv.Itoa(int(scid)), 53 | scl, 54 | lang, 55 | league, 56 | maxScore, 57 | } 58 | } 59 | 60 | func DefaultLeaderboardEntry(uid string) LeaderboardEntry { 61 | return NewLeaderboardEntry( 62 | uid, 63 | "", 64 | "", 65 | 0, 66 | 1, 67 | 0, 68 | 0, 69 | 0, 70 | 0, 71 | 0, 72 | time.Now().UTC().Unix(), // this should be player.LastLogin! 73 | 0, 74 | 0, 75 | 0, 76 | 0, 77 | 0, 78 | 0, 79 | 0, 80 | 0, 81 | 1, 82 | 0, 83 | 0, 84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /obj/leagueData.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type LeagueData struct { 4 | LeagueID int64 `json:"leagueId,string"` 5 | GroupID int64 `json:"groupId,string"` 6 | NumUp int64 `json:"numUp,string"` 7 | NumDown int64 `json:"numDown,string"` 8 | NumGroupMember int64 `json:"numGroupMember,string"` 9 | HighScoreOperator []OperatorScore `json:"highScoreOpe"` // what is this used for? 10 | TotalScoreOperator []OperatorScore `json:"totalScoreOpe"` // ^^^^^^^^^^^^^^^^^^^^ 11 | } 12 | 13 | func NewLeagueData(lid, gid, nup, ndown, ngm int64, hso, tso []OperatorScore) LeagueData { 14 | return LeagueData{ 15 | lid, 16 | gid, 17 | nup, 18 | ndown, 19 | ngm, 20 | hso, 21 | tso, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /obj/loginBonusReward.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type LoginBonusReward struct { 4 | SelectRewardList []SelectReward `json:"selectRewardList"` 5 | } 6 | 7 | func NewLoginBonusReward(selectRewardList []SelectReward) LoginBonusReward { 8 | return LoginBonusReward{ 9 | selectRewardList, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /obj/loginBonusStatus.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type LoginBonusStatus struct { 4 | NumLogin int64 `json:"numLogin"` // how many logins 5 | NumBonus int64 `json:"numBonus"` // how many bonuses? 6 | LastBonusTime int64 `json:"lastBonusTime"` // last time gotten a bonus (last login, essentially) 7 | } 8 | 9 | func NewLoginBonusStatus(numLogin, numBonus, lastBonusTime int64) LoginBonusStatus { 10 | return LoginBonusStatus{ 11 | numLogin, 12 | numBonus, 13 | lastBonusTime, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /obj/mapInfo.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | const ( 4 | DefaultMapDistance = 0 5 | DefaultNumBossAttack = 0 // Boss HP gets subtracted by this number, for some reason. 6 | DefaultStageDistance = 1337000 7 | DefaultStageMaxScore = 8008135 8 | ) 9 | 10 | type MapInfo struct { 11 | MapDistance int64 `json:"mapDistance"` // used sparingly in game...? 12 | NumBossAttack int64 `json:"numBossAttack"` // TODO: number of boss fights? Check how often this is used in game 13 | StageDistance int64 `json:"stageDistance"` // TODO: discover use 14 | StageMaxScore int64 `json:"stageMaxScore"` // TODO: discover use 15 | } 16 | 17 | func DefaultMapInfo() MapInfo { 18 | return MapInfo{ 19 | DefaultMapDistance, 20 | DefaultNumBossAttack, 21 | DefaultStageDistance, 22 | DefaultStageMaxScore, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /obj/message.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Message struct { 4 | ID string `json:"messageId"` 5 | Type int64 `json:"messageType"` 6 | FriendID string `json:"friendId"` 7 | Name string `json:"name"` 8 | URL string `json:"url"` 9 | Item MessageItem `json:"item"` 10 | ExpireTime int64 `json:"expireTime"` 11 | } 12 | -------------------------------------------------------------------------------- /obj/messageItem.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type MessageItem struct { 4 | ID int64 `json:"itemId,string"` 5 | Amount int64 `json:"numItem"` 6 | AdditionalInfo1 int64 `json:"additionalInfo1"` 7 | AdditionalInfo2 int64 `json:"additionalInfo2"` 8 | } 9 | 10 | func NewMessageItem(id, amount, ai1, ai2 int64) MessageItem { 11 | return MessageItem{ 12 | id, 13 | amount, 14 | ai1, 15 | ai2, 16 | } 17 | } 18 | 19 | func MessageItemToPresent(item MessageItem) Present { 20 | return NewPresent( 21 | item.ID, 22 | item.Amount, 23 | item.AdditionalInfo1, 24 | item.AdditionalInfo2, 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /obj/mileageIncentive.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/enums" 7 | ) 8 | 9 | type MileageIncentive struct { 10 | Type int64 `json:"type"` 11 | ItemID string `json:"itemId"` 12 | FriendID string `json:"friendId,omitempty"` 13 | NumItem int64 `json:"numItem"` 14 | PointID int64 `json:"pointId"` 15 | } 16 | 17 | func DefaultMileageIncentive() MileageIncentive { 18 | // TODO: this is ONLY for debugging right now. 19 | return MileageIncentive{ 20 | enums.IncentiveTypePoint, 21 | strconv.Itoa(int(enums.ItemIDInvincible)), 22 | "", // for battling? 23 | 2, 24 | 2, // ??? 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /obj/mileageReward.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/enums" 7 | ) 8 | 9 | type MileageReward struct { 10 | Type int64 `json:"type"` // never used in game? 11 | ItemID string `json:"itemId"` // TODO: integrate obj.Item as field instead of itemId and numItem? 12 | NumItem int64 `json:"numItem"` 13 | Point int64 `json:"point"` 14 | LimitTime int64 `json:"limitTime"` // timespan (sec.) 15 | } 16 | 17 | func DefaultMileageReward(point int64) MileageReward { 18 | return MileageReward{ 19 | enums.ItemTypeInvincible, 20 | strconv.Itoa(int(enums.ItemIDInvincible)), 21 | 1, 22 | point, 23 | 690, // 11 minutes, 30 seconds 24 | } 25 | } 26 | 27 | func NewMileageReward(itype, itemID, numItem, point int64) MileageReward { 28 | // TODO: Should not be used for legitimate purposes, only rapid development! 29 | return MileageReward{ 30 | itype, // should be enums.IncentiveType* 31 | strconv.Itoa(int(itemID)), 32 | numItem, 33 | point, 34 | -1, // no time limit 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /obj/operatorInformation.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | // TODO: create a param object that makes it easier to create informations 4 | 5 | type OperatorInformation struct { 6 | ID int64 `json:"id"` 7 | Content string `json:"content"` 8 | } 9 | 10 | func NewOperatorInformation(id int64, content string) OperatorInformation { 11 | return OperatorInformation{ 12 | id, 13 | content, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /obj/operatorMessage.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/RunnersRevival/outrun/enums" 8 | ) 9 | 10 | type OperatorMessage struct { 11 | ID string `json:"messageId"` 12 | Content string `json:"contents"` 13 | Item MessageItem `json:"item"` 14 | ExpireTime int64 `json:"expireTime"` 15 | } 16 | 17 | func DefaultOperatorMessage() OperatorMessage { 18 | id := "8575819" 19 | content := "A daily challenge reward." 20 | item := NewMessageItem( 21 | enums.ItemIDInvincible, 22 | 135, 23 | 0, 24 | 0, 25 | ) 26 | expireTime := time.Now().UTC().Unix() + 12600 // three and a half hours from now 27 | return OperatorMessage{ 28 | id, 29 | content, 30 | item, 31 | expireTime, 32 | } 33 | } 34 | 35 | func NewOperatorMessage(id int64, content string, item MessageItem, expiresAfter int64) OperatorMessage { 36 | expireTime := time.Now().UTC().Unix() + expiresAfter 37 | return OperatorMessage{ 38 | strconv.Itoa(int(id)), 39 | content, 40 | item, 41 | expireTime, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /obj/operatorScore.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type OperatorScore struct { 4 | Operator int64 `json:"operator,string"` 5 | Number int64 `json:"number,string"` 6 | PresentList []Item `json:"presentList,string"` 7 | } 8 | 9 | func NewOperatorScore(operator, number int64, presentList []Item) OperatorScore { 10 | return OperatorScore{ 11 | operator, 12 | number, 13 | presentList, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /obj/present.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type Present struct { 4 | ItemID int64 `json:"itemId"` 5 | NumItem int64 `json:"numItem"` 6 | AdditionalInfo1 int64 `json:"additionalInfo1"` 7 | AdditionalInfo2 int64 `json:"additionalInfo2"` 8 | } 9 | 10 | func NewPresent(itemId, numItem, additionalInfo1, additionalInfo2 int64) Present { 11 | return Present{ 12 | itemId, 13 | numItem, 14 | additionalInfo1, 15 | additionalInfo2, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /obj/raidbossPrize.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type RaidbossPrize struct { 4 | ItemID string `json:"itemId"` 5 | NumItem int64 `json:"numItem,string"` 6 | ItemWeight int64 `json:"itemRate,string"` 7 | SpinID int64 `json:"spinId,string"` 8 | } 9 | 10 | func NewRaidbossPrize(id string, numitem, weight, spinid int64) RaidbossPrize { 11 | return RaidbossPrize{ 12 | id, 13 | numitem, 14 | weight, 15 | spinid, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /obj/raidbossUser.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | ) 7 | 8 | type RaidbossUserState struct { 9 | WrestleID string `json:"friendId"` 10 | Name string `json:"name"` 11 | Grade int64 `json:"grade"` 12 | NumRank int64 `json:"numRank"` 13 | LoginTime int64 `json:"loginTime"` 14 | CharacterID string `json:"charaId"` 15 | CharacterLevel int64 `json:"characterLevel"` 16 | SubcharacterID string `json:"subCharaId"` 17 | SubcharacterLevel int64 `json:"subCharaLevel"` 18 | MainChaoID string `json:"mainChaoId"` 19 | MainChaoLevel int64 `json:"mainChaoLevel"` 20 | SubChaoID string `json:"subChaoId"` 21 | SubChaoLevel int64 `json:"subChaoLevel"` 22 | Language int64 `json:"language"` // enums.Lang* 23 | League int64 `json:"league"` 24 | WrestleCount int64 `json:"wrestleCount"` 25 | WrestleDamage int64 `json:"wrestleDamage"` 26 | WrestleBeatFlg int64 `json:"wrestleBeatFlg"` 27 | } 28 | 29 | func NewRaidbossUserState(wid, n string, g, nr, lt, cid, cl, schid, schl, mcid, mcl, scid, scl, lang, league, wc, wd, wbf int64) RaidbossUserState { 30 | return RaidbossUserState{ 31 | wid, 32 | n, 33 | g, 34 | nr, 35 | lt, 36 | strconv.Itoa(int(cid)), 37 | cl, 38 | strconv.Itoa(int(schid)), 39 | schl, 40 | strconv.Itoa(int(mcid)), 41 | mcl, 42 | strconv.Itoa(int(scid)), 43 | scl, 44 | lang, 45 | league, 46 | wc, 47 | wd, 48 | wbf, 49 | } 50 | } 51 | 52 | func DefaultRaidbossUserState(uid string) RaidbossUserState { 53 | return NewRaidbossUserState( 54 | uid, 55 | "", 56 | 1, 57 | 0, 58 | time.Now().UTC().Unix(), 59 | 0, 60 | 0, 61 | 0, 62 | 0, 63 | 0, 64 | 0, 65 | 0, 66 | 0, 67 | 1, 68 | 0, 69 | 0, 70 | 0, 71 | 0, 72 | ) 73 | } 74 | -------------------------------------------------------------------------------- /obj/redStarItem.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type RedStarItem struct { 4 | StoreItemID string `json:"storeItemId"` 5 | ItemID string `json:"itemId"` 6 | PriceDisp string `json:"priceDisp"` 7 | ProductID string `json:"productId"` 8 | NumItem int64 `json:"numItem"` 9 | Price int64 `json:"price"` 10 | Campaign *Campaign `json:"campaign"` // use nil to remove campaign. But does this make the game freak out...? 11 | } 12 | 13 | func NewRedStarItem(storeItemId, itemId, priceDisp, productID string, numItem, price int64, campaign *Campaign) RedStarItem { 14 | return RedStarItem{ 15 | storeItemId, 16 | itemId, 17 | priceDisp, 18 | productID, 19 | numItem, 20 | price, 21 | campaign, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /obj/requests_generic.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type LineAuth struct { 4 | UserID string `json:"userId"` 5 | Password string `json:"password"` 6 | MigrationPassword string `json:"migrationPassword"` 7 | MigrationUserPassword string `json:"migrationUserPassword"` 8 | } 9 | -------------------------------------------------------------------------------- /obj/selectReward.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | type SelectReward struct { 4 | ItemList []Item `json:"itemList"` 5 | } 6 | 7 | func NewSelectReward(itemList []Item) SelectReward { 8 | return SelectReward{ 9 | itemList, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /obj/ticker.go: -------------------------------------------------------------------------------- 1 | package obj 2 | 3 | import "time" 4 | 5 | type Ticker struct { 6 | ID int64 `json:"id"` 7 | Start int64 `json:"start"` 8 | End int64 `json:"end"` 9 | Param string `json:"param"` 10 | } 11 | 12 | func NewTicker(id, end int64, param string) Ticker { 13 | start := time.Now().UTC().Unix() 14 | return Ticker{ 15 | id, 16 | start, 17 | end, 18 | param, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /options/characterOptions.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | // Default values for characters 4 | // TODO: Fill out 5 | const () 6 | -------------------------------------------------------------------------------- /orpc/base.go: -------------------------------------------------------------------------------- 1 | package orpc 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "net/http" 7 | "net/rpc" 8 | 9 | "github.com/RunnersRevival/outrun/config" 10 | "github.com/RunnersRevival/outrun/orpc/rpcobj" 11 | ) 12 | 13 | func Start() { 14 | rpc.Register(new(rpcobj.Toolbox)) 15 | rpc.Register(new(rpcobj.Config)) 16 | rpc.HandleHTTP() 17 | listenStr := "127.0.0.1:" + config.CFile.RPCPort 18 | listener, err := net.Listen("tcp", listenStr) 19 | if err != nil { 20 | log.Fatal("error listening in ORPC:", err) 21 | } 22 | log.Println("Starting ORPC server on: " + listenStr) 23 | go http.Serve(listener, nil) 24 | } 25 | -------------------------------------------------------------------------------- /orpc/rpcobj/config.go: -------------------------------------------------------------------------------- 1 | package rpcobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/config" 5 | "github.com/RunnersRevival/outrun/config/eventconf" 6 | "github.com/RunnersRevival/outrun/config/infoconf" 7 | ) 8 | 9 | type Config struct { 10 | } 11 | 12 | func (c *Config) ReloadAllConfigs(nothing bool, reply *ConfigReply) error { 13 | err := config.Parse("config.json") 14 | if err != nil { 15 | reply.ConfigSuccess = false 16 | reply.ConfigError = err.Error() 17 | } else { 18 | reply.ConfigSuccess = true 19 | } 20 | 21 | err = eventconf.Parse(config.CFile.EventConfigFilename) 22 | if err != nil { 23 | reply.EventConfigSuccess = false 24 | reply.EventConfigError = err.Error() 25 | } else { 26 | reply.EventConfigSuccess = true 27 | } 28 | 29 | err = infoconf.Parse(config.CFile.InfoConfigFilename) 30 | if err != nil { 31 | reply.InfoConfigSuccess = false 32 | reply.InfoConfigError = err.Error() 33 | } else { 34 | reply.InfoConfigSuccess = true 35 | } 36 | return nil 37 | } 38 | 39 | func (c *Config) ReloadConfig(nothing bool, reply *ConfigReply) error { 40 | err := config.Parse("config.json") 41 | if err != nil { 42 | reply.ConfigSuccess = false 43 | reply.ConfigError = err.Error() 44 | } else { 45 | reply.ConfigSuccess = true 46 | } 47 | return nil 48 | } 49 | 50 | func (c *Config) ReloadEventConfig(nothing bool, reply *ConfigReply) error { 51 | err := eventconf.Parse(config.CFile.EventConfigFilename) 52 | if err != nil { 53 | reply.EventConfigSuccess = false 54 | reply.EventConfigError = err.Error() 55 | } else { 56 | reply.EventConfigSuccess = true 57 | } 58 | return nil 59 | } 60 | 61 | func (c *Config) ReloadInfoConfig(nothing bool, reply *ConfigReply) error { 62 | err := infoconf.Parse(config.CFile.InfoConfigFilename) 63 | if err != nil { 64 | reply.InfoConfigSuccess = false 65 | reply.InfoConfigError = err.Error() 66 | } else { 67 | reply.InfoConfigSuccess = true 68 | } 69 | return nil 70 | } 71 | 72 | type ConfigReply struct { 73 | ConfigSuccess bool 74 | ConfigError string 75 | EventConfigSuccess bool 76 | EventConfigError string 77 | InfoConfigSuccess bool 78 | InfoConfigError string 79 | } 80 | -------------------------------------------------------------------------------- /orpc/rpcobj/status.go: -------------------------------------------------------------------------------- 1 | package rpcobj 2 | 3 | const ( 4 | StatusOK = iota 5 | StatusUnknownError 6 | StatusOtherError 7 | ) 8 | -------------------------------------------------------------------------------- /orpc/rpcobj/toolbox.go: -------------------------------------------------------------------------------- 1 | package rpcobj 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/db" 5 | "github.com/RunnersRevival/outrun/netobj" 6 | "github.com/RunnersRevival/outrun/obj" 7 | ) 8 | 9 | type Toolbox struct { 10 | } 11 | 12 | func (t *Toolbox) RegisterPlayerWithID(uid string, reply *ToolboxReply) error { 13 | player := db.NewAccountWithID(uid, 0) 14 | err := db.SavePlayer(player) 15 | if err != nil { 16 | reply.Status = StatusOtherError 17 | reply.Info = "unable to save player: " + err.Error() 18 | return err 19 | } 20 | reply.Status = StatusOK 21 | reply.Info = "OK" 22 | return nil 23 | } 24 | 25 | func (t *Toolbox) FetchPlayer(uid string, reply *netobj.Player) error { 26 | player, err := db.GetPlayer(uid) 27 | if err != nil { 28 | return err 29 | } 30 | *reply = player 31 | return nil 32 | } 33 | 34 | type ToolboxReply struct { 35 | Status uint 36 | Info string 37 | } 38 | 39 | type ToolboxValueReply struct { 40 | Status uint 41 | Result interface{} 42 | } 43 | 44 | type ToolboxPlayerCountReply struct { 45 | Status uint 46 | ActiveCount int64 47 | TotalRegisteredCount int64 48 | AdditionalInfo string 49 | } 50 | 51 | type ChangeValueArgs struct { 52 | UID string 53 | Value interface{} 54 | } 55 | 56 | type SendOperatorMessageToAllArgs struct { 57 | MessageContents string 58 | Item obj.MessageItem 59 | ExpiresAfter int64 60 | } 61 | 62 | type SendOperatorMessageArgs struct { 63 | UID string 64 | MessageContents string 65 | Item obj.MessageItem 66 | ExpiresAfter int64 67 | } 68 | 69 | type SendOperatorMessageToSomeArgs struct { 70 | UIDs string 71 | MessageContents string 72 | Item obj.MessageItem 73 | ExpiresAfter int64 74 | } 75 | 76 | type ChangeCharacter struct { 77 | ID string `json:"characterId"` 78 | Cost int64 `json:"numRings"` // interestingly, is used for both buying the character and for levelling up... 79 | NumRedRings int64 `json:"numRedRings"` // ? 80 | Price int64 `json:"priceNumRings"` // used to limit smash, as far as I can tell? 81 | PriceRedRings int64 `json:"priceNumRedRings"` // ? 82 | LockCondition int64 83 | UIDs string 84 | } 85 | -------------------------------------------------------------------------------- /orpc/rpcobj/toolboxGetters.go: -------------------------------------------------------------------------------- 1 | package rpcobj 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/RunnersRevival/outrun/db" 8 | ) 9 | 10 | func (t *Toolbox) GetUsername(uid string, reply *ToolboxReply) error { 11 | player, err := db.GetPlayer(uid) 12 | if err != nil { 13 | reply.Status = StatusOtherError 14 | reply.Info = "unable to get player: " + err.Error() 15 | return err 16 | } 17 | reply.Status = StatusOK 18 | reply.Info = player.Username 19 | return nil 20 | } 21 | 22 | func (t *Toolbox) GetRouletteTickets(uid string, reply *ToolboxReply) error { 23 | player, err := db.GetPlayer(uid) 24 | if err != nil { 25 | reply.Status = StatusOtherError 26 | reply.Info = "unable to get player: " + err.Error() 27 | return err 28 | } 29 | reply.Status = StatusOK 30 | reply.Info = strconv.Itoa(int(player.PlayerState.NumRouletteTicket)) 31 | return nil 32 | } 33 | 34 | func (t *Toolbox) GetLastLogin(uid string, reply *ToolboxReply) error { 35 | player, err := db.GetPlayer(uid) 36 | if err != nil { 37 | reply.Status = StatusOtherError 38 | reply.Info = "unable to get player: " + err.Error() 39 | return err 40 | } 41 | reply.Status = StatusOK 42 | reply.Info = strconv.Itoa(int(player.LastLogin)) 43 | return nil 44 | } 45 | 46 | func (t *Toolbox) GetCurrentTeam(uid string, reply *ToolboxReply) error { 47 | player, err := db.GetPlayer(uid) 48 | if err != nil { 49 | reply.Status = StatusOtherError 50 | reply.Info = "unable to get player: " + err.Error() 51 | return err 52 | } 53 | result := []string{player.PlayerState.MainCharaID, player.PlayerState.SubCharaID} 54 | reply.Status = StatusOK 55 | reply.Info = strings.Join(result, ",") 56 | return nil 57 | } 58 | 59 | func (t *Toolbox) GetPersonalEvents(args ChangeValueArgs, reply *ToolboxValueReply) error { 60 | player, err := db.GetPlayer(args.UID) 61 | if err != nil { 62 | reply.Status = StatusOtherError 63 | reply.Result = "unable to get player: " + err.Error() 64 | return err 65 | } 66 | reply.Status = StatusOK 67 | reply.Result = player.PersonalEvents 68 | return nil 69 | } 70 | 71 | func (t *Toolbox) GetStoryHighScore(uid string, reply *ToolboxReply) error { 72 | player, err := db.GetPlayer(uid) 73 | if err != nil { 74 | reply.Status = StatusOtherError 75 | reply.Info = "unable to get player: " + err.Error() 76 | return err 77 | } 78 | reply.Status = StatusOK 79 | reply.Info = strconv.Itoa(int(player.PlayerState.HighScore)) 80 | return nil 81 | } 82 | 83 | func (t *Toolbox) GetQuickHighScore(uid string, reply *ToolboxReply) error { 84 | player, err := db.GetPlayer(uid) 85 | if err != nil { 86 | reply.Status = StatusOtherError 87 | reply.Info = "unable to get player: " + err.Error() 88 | return err 89 | } 90 | reply.Status = StatusOK 91 | reply.Info = strconv.Itoa(int(player.PlayerState.TimedHighScore)) 92 | return nil 93 | } 94 | 95 | func (t *Toolbox) GetEventCollectibleCount(uid string, reply *ToolboxReply) error { 96 | player, err := db.GetPlayer(uid) 97 | if err != nil { 98 | reply.Status = StatusOtherError 99 | reply.Info = "unable to get player: " + err.Error() 100 | return err 101 | } 102 | reply.Status = StatusOK 103 | reply.Info = strconv.Itoa(int(player.EventState.Param)) 104 | return nil 105 | } 106 | 107 | func (t *Toolbox) GetResetCount(uid string, reply *ToolboxReply) error { 108 | player, err := db.GetPlayer(uid) 109 | if err != nil { 110 | reply.Status = StatusOtherError 111 | reply.Info = "unable to get player: " + err.Error() 112 | return err 113 | } 114 | reply.Status = StatusOK 115 | reply.Info = strconv.Itoa(int(player.ResetCount)) 116 | return nil 117 | } 118 | 119 | func (t *Toolbox) GetTransferID(uid string, reply *ToolboxReply) error { 120 | player, err := db.GetPlayer(uid) 121 | if err != nil { 122 | reply.Status = StatusOtherError 123 | reply.Info = "unable to get player: " + err.Error() 124 | return err 125 | } 126 | reply.Status = StatusOK 127 | reply.Info = player.MigrationPassword 128 | return nil 129 | } 130 | -------------------------------------------------------------------------------- /requests/battle.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type GetDailyBattleHistoryRequest struct { 4 | Count int64 `json:"count,string"` 5 | } 6 | 7 | type ResetDailyBattleMatchingRequest struct { 8 | Type int64 `json:"type,string"` 9 | } -------------------------------------------------------------------------------- /requests/chao.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type EquipChaoRequest struct { 4 | Base 5 | MainChaoID string `json:"mainChaoId"` 6 | SubChaoID string `json:"subChaoId"` 7 | } 8 | 9 | type CommitChaoWheelSpinRequest struct { 10 | Base 11 | Count int64 `json:"count,string"` 12 | } 13 | -------------------------------------------------------------------------------- /requests/character.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type ChangeCharacterRequest struct { 4 | Base 5 | MainCharaID string `json:"mainCharacterId"` 6 | SubCharaID string `json:"subCharacterId"` 7 | } 8 | 9 | type UpgradeCharacterRequest struct { 10 | Base 11 | AbilityID string `json:"abilityId"` 12 | CharacterID string `json:"characterId"` 13 | } 14 | 15 | type UnlockedCharacterRequest struct { 16 | Base 17 | CharacterID string `json:"characterId"` 18 | ItemID string `json:"itemId"` 19 | } 20 | -------------------------------------------------------------------------------- /requests/event.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type GenericEventRequest struct { 4 | Base 5 | EventID int64 `json:"eventId,string"` 6 | } 7 | 8 | /*type EventActStartRequest struct { 9 | Base 10 | Modifier []string `json:"modifire"` // Seems to be list of item IDs. 11 | RaidbossID int64 `json:"raidbossId,string"` 12 | EventID int64 `json:"eventId,string"` 13 | EnergyExpend int64 `json:"energyExpend,string"` // the amount of raidboss energy to be used? 14 | } 15 | 16 | type EventPostGameResultsRequest struct { 17 | Base 18 | EventID int64 `json:"eventId,string"` 19 | NumRaidbossRings int64 `json:"numRaidbossRings,string"` 20 | } 21 | 22 | type EventUpdateGameResultsRequest struct { 23 | Base 24 | Score int64 `json:"score,string"` 25 | Rings int64 `json:"numRings,string"` 26 | FailureRings int64 `json:"numFailureRings,string"` 27 | RedRings int64 `json:"numRedStarRings,string"` 28 | Distance int64 `json:"distance,string"` 29 | DailyChallengeValue int64 `json:"dailyChallengeValue,string"` 30 | DailyChallengeComplete int64 `json:"dailyChallengeComplete,string"` 31 | Animals int64 `json:"numAnimals,string"` 32 | MaxCombo int64 `json:"maxCombo,string"` 33 | Closed int64 `json:"closed,string"` 34 | EventID int64 `json:"eventId,string"` 35 | EventValue int64 `json:"eventValue,string"` 36 | RaidbossID int64 `json:"raidbossId,string"` 37 | RaidbossDamage int64 `json:"raidbossDamage,string"` 38 | RaidbossBeatFlg int64 `json:"raidbossBeatFlg,string"` 39 | }*/ 40 | -------------------------------------------------------------------------------- /requests/friend.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type FacebookIncentiveRequest struct { 4 | Base 5 | Type int64 `json:"type,string"` 6 | AchievementCount int64 `json:"achievementCount,string"` 7 | } 8 | -------------------------------------------------------------------------------- /requests/game.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import "github.com/RunnersRevival/outrun/netobj" 4 | 5 | type QuickPostGameResultsRequest struct { 6 | Base 7 | Score int64 `json:"score,string"` 8 | Rings int64 `json:"numRings,string"` 9 | FailureRings int64 `json:"numFailureRings,string"` 10 | RedRings int64 `json:"numRedStarRings,string"` 11 | Distance int64 `json:"distance,string"` 12 | DailyChallengeValue int64 `json:"dailyChallengeValue,string"` 13 | DailyChallengeComplete int64 `json:"dailyChallengeComplete"` 14 | Animals int64 `json:"numAnimals,string"` 15 | MaxCombo int64 `json:"maxCombo,string"` 16 | Closed int64 `json:"closed"` 17 | CheatResult string `json:"cheatResult"` 18 | } 19 | 20 | type PostGameResultsRequest struct { 21 | QuickPostGameResultsRequest 22 | BossDestroyed int64 `json:"bossDestroyed"` 23 | ChapterClear int64 `json:"chapterClear"` 24 | GetChaoEgg int64 `json:"getChaoEgg"` 25 | NumBossAttack int64 `json:"numBossAttack,string"` 26 | ReachPoint int64 `json:"reachPoint,string"` 27 | EventID int64 `json:"eventId,string,omitempty"` 28 | EventValue int64 `json:"eventValue,string,omitempty"` 29 | } 30 | 31 | type QuickActStartRequest struct { 32 | Base 33 | Modifier []int64 `json:"modifire"` // Seems to be list of item IDs. 34 | Tutorial int64 `json:"tutorial,omitempty"` // will omit the field if not found 35 | } 36 | 37 | type ActStartRequest struct { 38 | QuickActStartRequest 39 | DistanceFriendList []netobj.MileageFriend `json:"distanceFriendList"` // TODO: Discover correct type... This might be list of strings 40 | EventID string `json:"eventId,omitempty"` 41 | } 42 | 43 | type MileageRewardRequest struct { 44 | Base 45 | Episode int64 `json:"episode,string"` 46 | Chapter int64 `json:"chapter,string"` 47 | } 48 | -------------------------------------------------------------------------------- /requests/generic.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type Base struct { 4 | SessionID string `json:"sessionId"` 5 | Version string `json:"version"` 6 | RevivalVerID int64 `json:"revivalVerId,string"` 7 | Seq int64 `json:"seq,string"` 8 | } 9 | -------------------------------------------------------------------------------- /requests/leaderboard.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type LeaderboardRequest struct { 4 | Base 5 | Mode int64 `json:"mode,string"` 6 | } 7 | 8 | type LeaderboardEntriesRequest struct { 9 | Base 10 | Mode int64 `json:"mode,string"` 11 | First int64 `json:"first,string"` 12 | Count int64 `json:"count,string"` 13 | Type int64 `json:"type,string"` 14 | FriendIDList []string `json:"friendIdList"` 15 | } 16 | -------------------------------------------------------------------------------- /requests/login.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import "github.com/RunnersRevival/outrun/obj" 4 | 5 | type LoginRequest struct { 6 | Version string `json:"version"` 7 | Device string `json:"device"` 8 | Seq int64 `json:"seq,string"` 9 | Platform int64 `json:"platform,string"` 10 | Language int64 `json:"language,string"` 11 | SalesLocate int64 `json:"salesLocate,string"` 12 | StoreID int64 `json:"storeId,string"` 13 | PlatformSNS int64 `json:"platform_sns,string"` 14 | RevivalVerID int64 `json:"revivalVerId,string"` 15 | obj.LineAuth `json:"lineAuth"` 16 | } 17 | 18 | type LoginBonusSelectRequest struct { 19 | RewardID int64 `json:"rewardId,string"` 20 | RewardDays int64 `json:"rewardDays,string"` 21 | RewardSelect int64 `json:"rewardSelect,string"` 22 | FirstRewardDays int64 `json:"firstRewardDays,string"` 23 | FirstRewardSelect int64 `json:"firstRewardSelect,string"` 24 | } 25 | 26 | type GetMigrationPasswordRequest struct { 27 | UserPassword string `json:"userPassword"` 28 | } 29 | -------------------------------------------------------------------------------- /requests/message.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type GetMessageRequest struct { 4 | Base 5 | MessageIDs interface{} `json:"messageId"` // can either be a list of int64s or "0" 6 | OperatorMessageIDs interface{} `json:"operationMessageId"` // can either be a list of int64s or "0" 7 | } 8 | -------------------------------------------------------------------------------- /requests/player.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type SetUsernameRequest struct { 4 | Base 5 | Username string `json:"userName"` 6 | } 7 | -------------------------------------------------------------------------------- /requests/raidbossSpin.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type ItemStockNumRequest struct { 4 | Base 5 | EventID int64 `json:"eventId,string"` 6 | ItemIDs []int64 `json:"itemIdList"` 7 | } 8 | -------------------------------------------------------------------------------- /requests/sgn.go: -------------------------------------------------------------------------------- 1 | package requests 2 | -------------------------------------------------------------------------------- /requests/spin.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type CommitWheelSpinRequest struct { 4 | Base 5 | Count int64 `json:"count,string"` 6 | } 7 | -------------------------------------------------------------------------------- /requests/store.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | type RedStarExchangeListRequest struct { 4 | Base 5 | ItemType int64 `json:"itemType,string"` 6 | } 7 | 8 | type RedStarExchange struct { 9 | Base 10 | ItemID string `json:"itemId"` 11 | } 12 | -------------------------------------------------------------------------------- /responses/apollo.go: -------------------------------------------------------------------------------- 1 | package responses 2 | -------------------------------------------------------------------------------- /responses/baseResponse.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/consts" 5 | "github.com/RunnersRevival/outrun/meta" 6 | "github.com/RunnersRevival/outrun/responses/responseobjs" 7 | ) 8 | 9 | type BaseResponse struct { 10 | responseobjs.BaseInfo 11 | AssetsVersion string `json:"assets_version"` // doesn't necessarily have to be a number 12 | ClientDataVersion string `json:"client_data_version"` 13 | DataVersion string `json:"data_version"` 14 | InfoVersion string `json:"info_version"` 15 | Version string `json:"version"` 16 | OutrunVersion string `json:"ORN_version"` 17 | } 18 | 19 | func NewBaseResponse(base responseobjs.BaseInfo) BaseResponse { 20 | return BaseResponse{ 21 | base, 22 | "054", 23 | "2.2.3", 24 | "15", 25 | "017", 26 | "2.2.3", 27 | meta.Version, 28 | } 29 | } 30 | 31 | func NewBaseResponseV(base responseobjs.BaseInfo, gameVersion string) BaseResponse { 32 | return BaseResponse{ 33 | base, 34 | consts.DataVersionForGameVersion[gameVersion], 35 | gameVersion, 36 | "15", 37 | "017", 38 | gameVersion, 39 | meta.Version, 40 | } 41 | } 42 | 43 | type NextVersionResponse struct { 44 | responseobjs.BaseInfo 45 | NumRedRingsIOS int64 `json:"numRedRingsIOS,string"` // UNCONFIRMED! 46 | NumRedRingsANDROID int64 `json:"numRedRingsANDROID,string"` 47 | NumBuyRedRingsIOS int64 `json:"numBuyRedRingsIOS,string"` // UNCONFIRMED! 48 | NumBuyRedRingsANDROID int64 `json:"numBuyRedRingsANDROID,string"` 49 | Username string `json:"userName"` 50 | CloseMessageJP string `json:"closeMessageJP"` 51 | CloseMessageEN string `json:"closeMessageEN"` 52 | CloseURL string `json:"closeUrl"` 53 | } 54 | 55 | func NewNextVersionResponse(base responseobjs.BaseInfo, numRedRings, numBuyRedRings int64, username, japaneseMessage, englishMessage, url string) NextVersionResponse { 56 | return NextVersionResponse{ 57 | base, 58 | numRedRings, 59 | numRedRings, 60 | numBuyRedRings, 61 | numBuyRedRings, 62 | username, 63 | japaneseMessage, 64 | englishMessage, 65 | url, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /responses/chao.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/RunnersRevival/outrun/consts" 7 | "github.com/RunnersRevival/outrun/enums" 8 | "github.com/RunnersRevival/outrun/netobj" 9 | "github.com/RunnersRevival/outrun/obj" 10 | "github.com/RunnersRevival/outrun/responses/responseobjs" 11 | ) 12 | 13 | type ChaoWheelOptionsResponse struct { 14 | BaseResponse 15 | ChaoWheelOptions netobj.ChaoWheelOptions `json:"chaoWheelOptions"` 16 | } 17 | 18 | func ChaoWheelOptions(base responseobjs.BaseInfo, chaoWheelOptions netobj.ChaoWheelOptions) ChaoWheelOptionsResponse { 19 | baseResponse := NewBaseResponse(base) 20 | out := ChaoWheelOptionsResponse{ 21 | baseResponse, 22 | chaoWheelOptions, 23 | } 24 | return out 25 | } 26 | 27 | func DefaultChaoWheelOptions(base responseobjs.BaseInfo, player netobj.Player) ChaoWheelOptionsResponse { 28 | // TODO: Assess if needed 29 | chaoWheelOptions := player.ChaoRouletteGroup.ChaoWheelOptions 30 | return ChaoWheelOptions( 31 | base, 32 | chaoWheelOptions, 33 | ) 34 | } 35 | 36 | type PrizeChaoWheelResponse struct { 37 | BaseResponse 38 | PrizeList []obj.ChaoPrize `json:"prizeList"` 39 | } 40 | 41 | func PrizeChaoWheel(base responseobjs.BaseInfo, prizeList []obj.ChaoPrize) PrizeChaoWheelResponse { 42 | baseResponse := NewBaseResponse(base) 43 | out := PrizeChaoWheelResponse{ 44 | baseResponse, 45 | prizeList, 46 | } 47 | return out 48 | } 49 | 50 | func DefaultPrizeChaoWheel(base responseobjs.BaseInfo) PrizeChaoWheelResponse { 51 | //prizeList := constobjs.DefaultChaoPrizeWheelPrizeList 52 | prizeList := []obj.ChaoPrize{} 53 | chaoids := []string{} 54 | chaorarities := []int64{} 55 | for chid := range consts.RandomChaoWheelChaoPrizes { 56 | rarity, _ := strconv.Atoi(string(chid[2])) 57 | chaoids = append(chaoids, chid) 58 | chaorarities = append(chaorarities, int64(rarity)) 59 | } 60 | for chid := range consts.RandomChaoWheelCharacterPrizes { 61 | chaoids = append(chaoids, chid) 62 | chaorarities = append(chaorarities, int64(100)) 63 | } 64 | for index := range chaoids { 65 | prizeList = append(prizeList, obj.NewChaoPrize(chaoids[index], chaorarities[index])) 66 | } 67 | return PrizeChaoWheel(base, prizeList) 68 | } 69 | 70 | type EquipChaoResponse struct { 71 | BaseResponse 72 | PlayerState netobj.PlayerState `json:"playerState"` 73 | } 74 | 75 | func EquipChao(base responseobjs.BaseInfo, playerState netobj.PlayerState) EquipChaoResponse { 76 | baseResponse := NewBaseResponse(base) 77 | return EquipChaoResponse{ 78 | baseResponse, 79 | playerState, 80 | } 81 | } 82 | 83 | type ChaoWheelSpinResponse struct { 84 | BaseResponse 85 | PlayerState netobj.PlayerState `json:"playerState"` 86 | CharacterState []netobj.Character `json:"characterState"` 87 | ChaoState []netobj.Chao `json:"chaoState"` // also works with json:"chaoStatus" 88 | ChaoWheelOptions netobj.ChaoWheelOptions `json:"chaoWheelOptions"` 89 | ChaoSpinResults []netobj.ChaoSpinResult `json:"chaoSpinResultList"` // Should only contain one element! Otherwise, ItemWon is interpreted as -1 90 | } 91 | 92 | func ChaoWheelSpin(base responseobjs.BaseInfo, playerState netobj.PlayerState, characterState []netobj.Character, chaoState []netobj.Chao, chaoWheelOptions netobj.ChaoWheelOptions, chaoSpinResults []netobj.ChaoSpinResult) ChaoWheelSpinResponse { 93 | baseResponse := NewBaseResponse(base) 94 | return ChaoWheelSpinResponse{ 95 | baseResponse, 96 | playerState, 97 | characterState, 98 | chaoState, 99 | chaoWheelOptions, 100 | chaoSpinResults, 101 | } 102 | } 103 | 104 | func DefaultChaoWheelSpin(base responseobjs.BaseInfo, player netobj.Player) ChaoWheelSpinResponse { 105 | // WARN: Do not use for normal purposes!! This should only be used for debugging 106 | dummyPrize := netobj.CharacterIDToChaoSpinPrize(enums.CTStrShadow) 107 | chaoSpinResults := netobj.DefaultChaoSpinResultNoItems(dummyPrize) 108 | return ChaoWheelSpin( 109 | base, 110 | player.PlayerState, 111 | player.CharacterState, 112 | player.ChaoState, 113 | player.ChaoRouletteGroup.ChaoWheelOptions, 114 | []netobj.ChaoSpinResult{chaoSpinResults}, 115 | ) 116 | } 117 | -------------------------------------------------------------------------------- /responses/character.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/responses/responseobjs" 6 | ) 7 | 8 | type ChangeCharacterResponse struct { 9 | BaseResponse 10 | PlayerState netobj.PlayerState `json:"playerState"` 11 | } 12 | 13 | func ChangeCharacter(base responseobjs.BaseInfo, playerState netobj.PlayerState) ChangeCharacterResponse { 14 | baseResponse := NewBaseResponse(base) 15 | return ChangeCharacterResponse{ 16 | baseResponse, 17 | playerState, 18 | } 19 | } 20 | 21 | type UpgradeCharacterResponse struct { 22 | BaseResponse 23 | PlayerState netobj.PlayerState `json:"playerState"` 24 | CharacterState []netobj.Character `json:"characterState"` 25 | } 26 | 27 | func UpgradeCharacter(base responseobjs.BaseInfo, playerState netobj.PlayerState, characterState []netobj.Character) UpgradeCharacterResponse { 28 | baseResponse := NewBaseResponse(base) 29 | return UpgradeCharacterResponse{ 30 | baseResponse, 31 | playerState, 32 | characterState, 33 | } 34 | } 35 | 36 | func DefaultUpgradeCharacter(base responseobjs.BaseInfo, player netobj.Player) UpgradeCharacterResponse { 37 | playerState := player.PlayerState 38 | characterState := player.CharacterState 39 | return UpgradeCharacter( 40 | base, 41 | playerState, 42 | characterState, 43 | ) 44 | } 45 | 46 | // UnlockedCharacter's response is the exact same as UpgradeCharacterResponse, it seems 47 | -------------------------------------------------------------------------------- /responses/friend.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/obj" 6 | "github.com/RunnersRevival/outrun/responses/responseobjs" 7 | ) 8 | 9 | type FacebookIncentiveResponse struct { 10 | BaseResponse 11 | PlayerState netobj.PlayerState `json:"playerState"` 12 | Presents []obj.Present `json:"incentive"` 13 | } 14 | 15 | func FacebookIncentive(base responseobjs.BaseInfo, playerState netobj.PlayerState, presents []obj.Present) FacebookIncentiveResponse { 16 | baseResponse := NewBaseResponse(base) 17 | return FacebookIncentiveResponse{ 18 | baseResponse, 19 | playerState, 20 | presents, 21 | } 22 | } 23 | 24 | func DefaultFacebookIncentive(base responseobjs.BaseInfo, player netobj.Player) FacebookIncentiveResponse { 25 | playerState := player.PlayerState 26 | presents := []obj.Present{} // Naughty this year 27 | return FacebookIncentive( 28 | base, 29 | playerState, 30 | presents, 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /responses/leaderboard.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/logic/conversion" 5 | "github.com/RunnersRevival/outrun/netobj" 6 | "github.com/RunnersRevival/outrun/obj" 7 | "github.com/RunnersRevival/outrun/obj/constobjs" 8 | "github.com/RunnersRevival/outrun/responses/responseobjs" 9 | "github.com/jinzhu/now" 10 | ) 11 | 12 | type WeeklyLeaderboardOptionsResponse struct { 13 | BaseResponse 14 | Mode int64 `json:"mode"` // 0 == ENDLESS, 1 == QUICK 15 | Type int64 `json:"type"` // 0 == RankingScoreType.HIGH_SCORE, else == RankingScoreType.TOTAL_SCORE 16 | Param int64 `json:"param"` // seemingly unused 17 | StartTime int64 `json:"startTime"` // both times are also seemingly unused... 18 | ResetTime int64 `json:"resetTime"` 19 | } 20 | 21 | func WeeklyLeaderboardOptions(base responseobjs.BaseInfo, mode, ltype, param, startTime, resetTime int64) WeeklyLeaderboardOptionsResponse { 22 | baseResponse := NewBaseResponse(base) 23 | return WeeklyLeaderboardOptionsResponse{ 24 | baseResponse, 25 | mode, 26 | ltype, 27 | param, 28 | startTime, 29 | resetTime, 30 | } 31 | } 32 | 33 | func DefaultWeeklyLeaderboardOptions(base responseobjs.BaseInfo, mode int64) WeeklyLeaderboardOptionsResponse { 34 | startTime := now.BeginningOfDay().UTC().Unix() 35 | resetTime := startTime + 86400 // + 1 Day 36 | //ltype := int64(1) 37 | ltype := int64(0) 38 | //param := int64(0) 39 | param := int64(5) 40 | return WeeklyLeaderboardOptions(base, mode, ltype, param, startTime, resetTime) 41 | } 42 | 43 | type WeeklyLeaderboardEntriesResponse struct { 44 | BaseResponse 45 | PlayerEntry obj.LeaderboardEntry `json:"playerEntry"` 46 | LastOffset int64 `json:"lastOffset"` 47 | StartTime int64 `json:"startTime"` 48 | ResetTime int64 `json:"resetTime"` 49 | StartIndex int64 `json:"startIndex"` 50 | Mode int64 `json:"mode"` 51 | TotalEntries int64 `json:"totalEntries"` 52 | EntriesList []obj.LeaderboardEntry `json:"entriesList"` 53 | } 54 | 55 | func WeeklyLeaderboardEntries(base responseobjs.BaseInfo, pe obj.LeaderboardEntry, lo, st, rt, si, m, te int64, el []obj.LeaderboardEntry) WeeklyLeaderboardEntriesResponse { 56 | baseResponse := NewBaseResponse(base) 57 | out := WeeklyLeaderboardEntriesResponse{ 58 | baseResponse, 59 | pe, 60 | lo, 61 | st, 62 | rt, 63 | si, 64 | m, 65 | te, 66 | el, 67 | } 68 | return out 69 | } 70 | 71 | func DefaultWeeklyLeaderboardEntries(base responseobjs.BaseInfo, player netobj.Player, mode, lbtype int64) WeeklyLeaderboardEntriesResponse { 72 | startTime := now.BeginningOfWeek().UTC().Unix() 73 | resetTime := now.EndOfWeek().UTC().Unix() 74 | myEntry := conversion.PlayerToLeaderboardEntry(player, mode, lbtype) 75 | return WeeklyLeaderboardEntries( 76 | base, 77 | //obj.DefaultLeaderboardEntry(uid), 78 | myEntry, 79 | -1, 80 | startTime, 81 | resetTime, 82 | 1, 83 | mode, 84 | 0, 85 | []obj.LeaderboardEntry{ 86 | myEntry, 87 | }, 88 | ) 89 | } 90 | 91 | type LeagueDataResponse struct { 92 | BaseResponse 93 | LeagueData obj.LeagueData `json:"leagueData"` 94 | Mode int64 `json:"mode"` 95 | } 96 | 97 | func LeagueData(base responseobjs.BaseInfo, leagueData obj.LeagueData, mode int64) LeagueDataResponse { 98 | baseResponse := NewBaseResponse(base) 99 | out := LeagueDataResponse{ 100 | baseResponse, 101 | leagueData, 102 | mode, 103 | } 104 | return out 105 | } 106 | 107 | func DefaultLeagueData(base responseobjs.BaseInfo, mode int64) LeagueDataResponse { 108 | var leagueData obj.LeagueData 109 | if mode == 0 { 110 | leagueData = constobjs.DefaultLeagueDataMode0 111 | } else if mode == 1 { 112 | leagueData = constobjs.DefaultLeagueDataMode1 113 | } 114 | return LeagueData(base, leagueData, mode) 115 | } 116 | -------------------------------------------------------------------------------- /responses/message.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/obj" 6 | "github.com/RunnersRevival/outrun/responses/responseobjs" 7 | ) 8 | 9 | type MessageListResponse struct { 10 | BaseResponse 11 | MessageList []obj.Message `json:"messageList"` 12 | TotalMessages int64 `json:"totalMessage"` 13 | OperatorMessageList []obj.OperatorMessage `json:"operatorMessageList"` 14 | TotalOperatorMessages int64 `json:"totalOperatorMessage"` 15 | } 16 | 17 | func MessageList(base responseobjs.BaseInfo, msgl []obj.Message, opmsgl []obj.OperatorMessage) MessageListResponse { 18 | baseResponse := NewBaseResponse(base) 19 | out := MessageListResponse{ 20 | baseResponse, 21 | msgl, 22 | int64(len(msgl)), 23 | opmsgl, 24 | int64(len(opmsgl)), 25 | } 26 | return out 27 | } 28 | 29 | func DefaultMessageList(base responseobjs.BaseInfo) MessageListResponse { 30 | return MessageList( 31 | base, 32 | []obj.Message{}, 33 | []obj.OperatorMessage{ 34 | obj.DefaultOperatorMessage(), 35 | }, 36 | ) 37 | } 38 | 39 | type GetMessageResponse struct { 40 | BaseResponse 41 | PlayerState netobj.PlayerState `json:"playerState"` 42 | CharacterState []netobj.Character `json:"characterState"` 43 | ChaoState []netobj.Chao `json:"chaoState"` 44 | PresentList []obj.Present `json:"presentList"` // obtained gifts? 45 | RemainingMessageIDs []int64 `json:"notRecvMessageList"` // IDs of messages not yet received? 46 | RemainingOperatorMessageIDs []int64 `json:"notRecvOperatorMessageList"` // IDs of operator messages not yet received? 47 | } 48 | 49 | func GetMessage(base responseobjs.BaseInfo, player netobj.Player, presentList []obj.Present, remainingMessageIDs, remainingOperatorMessageIDs []int64) GetMessageResponse { 50 | baseResponse := NewBaseResponse(base) 51 | out := GetMessageResponse{ 52 | baseResponse, 53 | player.PlayerState, 54 | player.CharacterState, 55 | player.ChaoState, 56 | presentList, 57 | remainingMessageIDs, 58 | remainingOperatorMessageIDs, 59 | } 60 | return out 61 | } 62 | -------------------------------------------------------------------------------- /responses/option.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/responses/responseobjs" 6 | ) 7 | 8 | type OptionUserResultResponse struct { 9 | BaseResponse 10 | OptionUserResult netobj.OptionUserResult `json:"optionUserResult"` 11 | } 12 | 13 | func OptionUserResult(base responseobjs.BaseInfo, optionUserResult netobj.OptionUserResult) OptionUserResultResponse { 14 | baseResponse := NewBaseResponse(base) 15 | out := OptionUserResultResponse{ 16 | baseResponse, 17 | optionUserResult, 18 | } 19 | return out 20 | } 21 | -------------------------------------------------------------------------------- /responses/player.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/responses/responseobjs" 6 | ) 7 | 8 | type PlayerStateResponse struct { 9 | BaseResponse 10 | PlayerState netobj.PlayerState `json:"playerState"` 11 | } 12 | 13 | func PlayerState(base responseobjs.BaseInfo, playerState netobj.PlayerState) PlayerStateResponse { 14 | baseResponse := NewBaseResponse(base) 15 | out := PlayerStateResponse{ 16 | baseResponse, 17 | playerState, 18 | } 19 | return out 20 | } 21 | 22 | type CharacterStateResponse struct { 23 | BaseResponse 24 | CharacterState []netobj.Character `json:"characterState"` 25 | } 26 | 27 | func CharacterState(base responseobjs.BaseInfo, characterState []netobj.Character) CharacterStateResponse { 28 | baseResponse := NewBaseResponse(base) 29 | out := CharacterStateResponse{ 30 | baseResponse, 31 | characterState, 32 | } 33 | return out 34 | } 35 | 36 | type ChaoStateResponse struct { 37 | BaseResponse 38 | ChaoState []netobj.Chao `json:"chaoState"` 39 | } 40 | 41 | func ChaoState(base responseobjs.BaseInfo, chaoState []netobj.Chao) ChaoStateResponse { 42 | baseResponse := NewBaseResponse(base) 43 | out := ChaoStateResponse{ 44 | baseResponse, 45 | chaoState, 46 | } 47 | return out 48 | } 49 | -------------------------------------------------------------------------------- /responses/raidbossSpin.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/obj" 5 | "github.com/RunnersRevival/outrun/obj/constobjs" 6 | "github.com/RunnersRevival/outrun/responses/responseobjs" 7 | ) 8 | 9 | type ItemStockNumResponse struct { 10 | BaseResponse 11 | ItemStockList []obj.Item `json:"itemStockList"` 12 | } 13 | 14 | func ItemStockNum(base responseobjs.BaseInfo, itemStockList []obj.Item) ItemStockNumResponse { 15 | baseResponse := NewBaseResponse(base) 16 | return ItemStockNumResponse{ 17 | baseResponse, 18 | itemStockList, 19 | } 20 | } 21 | 22 | func DefaultItemStockNum(base responseobjs.BaseInfo) ItemStockNumResponse { 23 | return ItemStockNum( 24 | base, 25 | constobjs.DefaultSpinItems, 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /responses/responseobjs/baseInfo.go: -------------------------------------------------------------------------------- 1 | package responseobjs 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/jinzhu/now" 7 | ) 8 | 9 | type BaseInfo struct { 10 | ErrorMessage ErrorMessage `json:"errorMessage,string"` 11 | CloseTime int64 `json:"closeTime"` // end of the day 12 | Seq int64 `json:"seq,string"` 13 | ServerTime int64 `json:"server_time"` 14 | StatusCode int64 `json:"statusCode"` 15 | } 16 | 17 | func (b BaseInfo) SetErrorMessage(message string) { 18 | b.ErrorMessage = ErrorMessage(message) 19 | } 20 | 21 | func NewBaseInfo(em string, statusCode int64) BaseInfo { 22 | // seq is a default 0 for now, since it does not impact gameplay thus far 23 | closeTime := now.EndOfDay().UTC().Unix() 24 | serverTime := time.Now().UTC().Unix() 25 | seq := int64(0) 26 | return BaseInfo{ 27 | ErrorMessage(em), 28 | closeTime, 29 | seq, 30 | serverTime, 31 | statusCode, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /responses/responseobjs/errorMessage.go: -------------------------------------------------------------------------------- 1 | package responseobjs 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type ErrorMessage string 8 | 9 | func (em ErrorMessage) MarshalJSON() ([]byte, error) { 10 | return []byte(strconv.QuoteToASCII(string(em))), nil 11 | } 12 | -------------------------------------------------------------------------------- /responses/sgn.go: -------------------------------------------------------------------------------- 1 | package responses 2 | -------------------------------------------------------------------------------- /responses/spin.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/responses/responseobjs" 6 | ) 7 | 8 | type WheelOptionsResponse struct { 9 | BaseResponse 10 | WheelOptions netobj.WheelOptions `json:"wheelOptions"` 11 | } 12 | 13 | func WheelOptions(base responseobjs.BaseInfo, wheelOptions netobj.WheelOptions) WheelOptionsResponse { 14 | baseResponse := NewBaseResponse(base) 15 | out := WheelOptionsResponse{ 16 | baseResponse, 17 | wheelOptions, 18 | } 19 | return out 20 | } 21 | 22 | type WheelSpinResponse struct { 23 | BaseResponse 24 | PlayerState netobj.PlayerState `json:"playerState"` 25 | CharacterState []netobj.Character `json:"characterState"` 26 | ChaoState []netobj.Chao `json:"chaoState"` 27 | WheelOptions netobj.WheelOptions `json:"wheelOptions"` 28 | } 29 | 30 | func WheelSpin(base responseobjs.BaseInfo, playerState netobj.PlayerState, characterState []netobj.Character, chaoState []netobj.Chao, wheelOptions netobj.WheelOptions) WheelSpinResponse { 31 | baseResponse := NewBaseResponse(base) 32 | return WheelSpinResponse{ 33 | baseResponse, 34 | playerState, 35 | characterState, 36 | chaoState, 37 | wheelOptions, 38 | } 39 | } 40 | 41 | /* 42 | func DefaultWheelSpin(base responseobjs.BaseInfo, player netobj.Player) WheelSpinResponse { 43 | // TODO: remove me! I am no longer being used. 44 | wheelOptions := netobj.DefaultWheelOptions(player.PlayerState.NumRouletteTicket, player.RouletteInfo.RouletteCountInPeriod) 45 | playerState := player.PlayerState 46 | characterState := player.CharacterState 47 | chaoState := player.ChaoState 48 | return WheelSpin( 49 | base, 50 | playerState, 51 | characterState, 52 | chaoState, 53 | wheelOptions, 54 | ) 55 | } 56 | */ 57 | -------------------------------------------------------------------------------- /responses/store.go: -------------------------------------------------------------------------------- 1 | package responses 2 | 3 | import ( 4 | "github.com/RunnersRevival/outrun/netobj" 5 | "github.com/RunnersRevival/outrun/obj" 6 | "github.com/RunnersRevival/outrun/responses/responseobjs" 7 | ) 8 | 9 | type RedStarExchangeListResponse struct { 10 | BaseResponse 11 | ItemList []obj.RedStarItem `json:"itemList"` 12 | TotalItems int64 `json:"totalItems"` 13 | MonthPurchase int64 `json:"monthPurchase"` 14 | Birthday string `json:"birthday"` 15 | } 16 | 17 | func RedStarExchangeList(base responseobjs.BaseInfo, itemList []obj.RedStarItem, monthPurchase int64, birthday string) RedStarExchangeListResponse { 18 | baseResponse := NewBaseResponse(base) 19 | totalItems := int64(len(itemList)) 20 | return RedStarExchangeListResponse{ 21 | baseResponse, 22 | itemList, 23 | totalItems, 24 | monthPurchase, 25 | birthday, 26 | } 27 | } 28 | 29 | func DefaultRedStarExchangeList(base responseobjs.BaseInfo) RedStarExchangeListResponse { 30 | itemList := []obj.RedStarItem{} 31 | monthPurchase := int64(0) 32 | birthday := "1900-1-1" 33 | return RedStarExchangeList(base, itemList, monthPurchase, birthday) 34 | } 35 | 36 | type RedStarExchangeResponse struct { 37 | BaseResponse 38 | PlayerState netobj.PlayerState `json:"playerState"` 39 | } 40 | 41 | func RedStarExchange(base responseobjs.BaseInfo, playerState netobj.PlayerState) RedStarExchangeResponse { 42 | baseResponse := NewBaseResponse(base) 43 | return RedStarExchangeResponse{ 44 | baseResponse, 45 | playerState, 46 | } 47 | } 48 | 49 | func DefaultRedStarExchange(base responseobjs.BaseInfo, player netobj.Player) RedStarExchangeResponse { 50 | playerState := player.PlayerState 51 | return RedStarExchange( 52 | base, 53 | playerState, 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /status/statuses.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | const ( 4 | OK = int64(0) 5 | ServerSecurityError = int64(-19001) 6 | VersionDifference = int64(-19002) 7 | DecryptionFailure = int64(-19003) 8 | ParamHashDifference = int64(-19004) 9 | ServerNextVersion = int64(-19990) 10 | ServerMaintenance = int64(-19997) 11 | ServerBusyError = int64(-19998) 12 | ServerSystemError = int64(-19999) 13 | RequestParamError = int64(-10100) 14 | NotAvailablePlayer = int64(-10101) 15 | MissingPlayer = int64(-10102) 16 | ExpiredSession = int64(-10103) 17 | InvalidPassword = int64(-10104) 18 | InvalidSerialCode = int64(-10105) 19 | UsedSerialCode = int64(-10106) 20 | HSPWebAPIError = int64(-10115) 21 | ApolloWebAPIError = int64(-10115) 22 | DataMismatch = int64(-30120) 23 | MasterDataMismatch = int64(-10121) 24 | NotEnoughRedRings = int64(-21030) 25 | NotEnoughRings = int64(-20131) 26 | NotEnoughEnergy = int64(-20132) 27 | RouletteUseLimit = int64(-30401) 28 | RouletteBoardReset = int64(-30411) 29 | CharacterLevelLimit = int64(-20601) 30 | AllChaoLevelLimit = int64(-20602) 31 | AlreadyInvitedFriend = int64(-30801) 32 | AlreadyRequestedEnergy = int64(-30901) 33 | AlreadySentEnergy = int64(-30902) 34 | ReceiveFailureMessage = int64(-30910) 35 | AlreadyExistedPrePurchase = int64(-11001) 36 | AlreadyRemovedPrePurchase = int64(-11002) 37 | InvalidReceiptData = int64(-11003) 38 | AlreadyProcessedReceipt = int64(-11004) 39 | EnergyLimitPurchaseTrigger = int64(-21010) 40 | NotStartEvent = int64(-10201) 41 | AlreadyEndEvent = int64(-10202) 42 | UsernameUnacceptable = int64(-40000) // 2.1.0 and later; generic server side rejection of username change 43 | UsernameTooLong = int64(-40001) // 2.1.0 and later; username not saved due to length 44 | UsernameHasNGWord = int64(-40002) // 2.1.0 and later; username not saved due to ng word 45 | DailyBattleRematchTooSoon = int64(-41000) // 2.2.4 and later; daily battle reroll cooldown still active 46 | VersionForApplication = int64(-999002) 47 | Timeout = int64(-7) 48 | OtherError = int64(-8) 49 | NotReachable = int64(-10) 50 | InvalidResponse = int64(-20) 51 | ClientError = int64(-400) 52 | InternalServerError = int64(-500) 53 | HSPPurchaseError = int64(-600) 54 | ServerBusy = int64(-700) // why different from other busy? 55 | ) 56 | --------------------------------------------------------------------------------