title: 'Yahu Server架構'
–-
tableObj.gameStatus = STATUS_BET_TIME; // 切成押注
tableObj.readFishScript(tableObj.gameMode); // 產魚腳本
tableObj.runLogicStart(); // 啟動計時器
檔案名稱 | 動作 | 觸發函式 | 內容 |
---|---|---|---|
gameHandler | 遊戲封包 | Handler.prototype.enterGame = function(msg, session, callback) | 進入遊戲封包=>檢查服務器是否在維護中 檢查是否已在遊玩其他遊戲 查詢或創建是否有gameuser 更新登入紀錄 寫入進入遊戲紀錄 取得玩家金幣數量 建立gameUser |
gameUserManager | 會員管理 | GameUserManager.join = function(userId, yahuId, money) | 進入大廳時, 建立會員物件和寫入Redis => 'GameUsers', 離開遊戲刪除redis |
gameUser | 會員管理 | var GameUser = function(pomelo_app, userId, yahuId, money) | 創造GameUser 物件 |
gameLobby | 配桌系統 | GameLobby.enterGame = function(yahuId, lobbyId, callback) | 進入遊戲時, 將玩家放入配對列表 self.enterGameList[yahuId] = lobbyId |
gameLobby | 配桌系統 | GameLobby.matchHandling = function(self) | 定時排程, 處理配桌工作(不斷呼叫自己的遞迴函式) |
gameTable | 桌內活動 | GameTable.joinTable = function(gameUser, pos, callback) | 處理加入此桌工作 |
gameTable | 桌內活動 | GameTable.RandGrpID = function() | 產生局號(格式為隨機26+9), grpId=IK-K7V9Y |
gameHandler | 遊戲封包 | Handler.bet = function(msg, session, callback) | 發射子彈, 單純蒐集封包資料, 沒處理邏輯 |
gameTable | 桌內活動 | GameTable.playerBet = function(pos, fortAngle, betLevel, bulletKind, lockFishId, callback) | 處理射子彈邏輯, 扣除金額產生,子彈編號, USER_SHOT |
gameTable | 桌內活動 | GameTable.playerCatchFish = function(pos, bulletId, fishIdList, linkFishList, callback) | 玩家中魚處理邏輯, BULLET_HIT_FISH |
gameTable | 桌內活動 | GameTable.runResult = function(pos, useMinPool, bulletIsNull, bulletData, validFishId, validFishDataId, validLinkFishId, validLinkFishDataId, linkageExplosionEnabled, linkFishEnabled, pirateShipEnabled) | 進行中魚計算 |
gameHandler | 遊戲封包 | Handler.leaveTable = function(msg, session, callback) | 呼叫大廳的函式 gameLobby.leaveGame |
gameTable | 桌內活動 | GameTable.userLeave = function (pos) | userLeaveFlag[pos]=true, 最後一個離開玩家,先將table上鎖 |
gameUserManager | 會員管理 | GameUserManager.join = function(userId, yahuId, money) | 離開大廳時, 刪除Redis => 'GameUsers' |
GameUser(描述登入會員的基本物件) 說明
欄位名稱 | 資料型態 | 細節 | 內容 |
---|---|---|---|
app | Obj | pomelo_app | 跟pomelo物件綁定, 可以取得相關物件 this.app.get("key"); |
pushService | Obj | this.PushService = this.app.get('PushService') | 配置訊息管理器 |
gameId | Obj | this.app.get("name") | (遊戲配置)server name |
gameConfig | Obj | conf_sys.GAME_DATA[this.gameId] | Game Configuration |
gameCategoryId | Obj | this.gameConfig.game_category_id | 待補 |
userId | string | this.userId = userId | GPK平台的userId |
yahuId | int | this.yahuId = yahuId | yahu自己生產的userId |
money | float | this.money = money | 看資料庫存的是float |
lobbyId | Obj | this.lobbyId = 0 | User所在的大廳編號 |
tableId | string | this.tableId = "" | User所在桌子編號 |
GameUser(函式說明)
| 函式名稱 | 細節 | 內容 |
| –––– | –––– | –––– |–––– |
| charge | function(data, callback) | 線上充值
會送出 self.pushService.pushByUID(self.yahuId, "CHARGE", message, utils.doNothing) |
GameUserManager(管理登入會員的物件) 說明
欄位名稱 | 資料型態 | 細節 | 內容 |
---|---|---|---|
app | Obj | this.app = pomelo_app | 同上 |
gameId | Obj | this.gameId = gameId | 登入的遊戲 |
gpkRedis | Obj | this.gpkRedis = this.app.get('gpkRedis') | redis控制器 |
gameUsers | Obj | this.gameUsers = {} | 登入的會員物件 |
GameUserManager(函式說明)
| 函式名稱 | 細節 | 內容 |
| –––– | –––– | –––– |–––– |
| clearRds | function() | 清除redis內的資料 key='GameUsers', 依照該玩家的yahuId去清除所有資料 |
| join | function(userId, yahuId, money) | 會員加入 |
| get | function(yahuId) | 取得會員 |
| leave | function(yahuId, callback) | 會員離開, 清除redis內的資料 key=‘GameUsers’, 依照該玩家的yahuId去清除資料 |
| count | function() | 取得目前會員數 |
GameLobby(描述進入和離開遊戲的基本物件) 說明
欄位名稱 | 資料型態 | 細節 | 內容 |
---|---|---|---|
app | Obj | pomelo_app | 同上 |
pushService | Obj | this.PushService = this.app.get('PushService') | 配置訊息管理器 |
gameId | Obj | this.gameId = this.app.get("name") | server id |
gameConfig | Obj | this.gameConfig = conf_sys.GAME_DATA[this.gameId] | Game Configuration |
theme | Obj | this.theme = theme; | |
gpkRedis | Obj | this.gpkRedis = this.app.get("gpkRedis") | redis控制器 |
enterGameList | Obj | this.enterGameList = {} | 遊戲配對相關結構 |
redisKey | Obj | this.redisKey = this.gameConfig.redisKey | Table ID相關設定 |
tableIDPrefix | Obj | this.tableIDPrefix = utils.dateFormat(new Date(), "yyyyMMdd") | Table ID相關設定 |
tableIdx | int | this.tableIdx = 0 | Table ID相關設定 |
lobbyNum | int | this.lobbyNum = this.gameConfig.lobbyNum | config/system.json 內的描述的大廳數量 |
lobbyUsers | Obj | this.lobbyUsers[i] = [] | 大廳內的User陣列 |
lobbyTables | Obj | this.lobbyTables[i] = {} | 大廳內的桌子陣列 |
match_mate_timer | Obj | this.match_mate_timer = null |
GameLobby(函式說明)
| 函式名稱 | 細節 | 內容 |
| –––– | –––– | –––– |–––– |
| start | function() | 啟動定時器, 一秒觸發 'matchHandling' 函式 一次 |
| enterGame | function(yahuId, lobbyId, callback) | 將玩家放入配對列表, self.enterGameList[yahuId] = lobbyId; |
| matchHandling | function(self) | 處理配對列表 1: 使用 TABLE_LIMIT:+GameId 搜尋redis內的資料
2:取得目前進入遊戲結構列表
3:處理配對 tableObj.joinTable
|
| leaveGame | function(yahuId, lobbyId, tableId, callback) | 離開遊戲檢查相關結構做清除
1: tableObj.userLeave(pos); // 清除GameTable的玩家
2: self.lobbyUsers[lobbyId] //清除玩家在廳內的狀態
3: 清除玩家GameUserManager資料 |
| broadcastHandling | function(lobbyId, data) | 處理廣播訊息
訊息塞入 self.PushService.pushByList(self.lobbyUsers[lobbyId], "GAME_BROADCAST", data, utils.doNothing) |
| charge | function (yahuId, lobbyId, tableId, amount, callback) | 線上充值, 呼叫GameTable的儲值函式 tableObj.charge(pos, amount) |
GameTable(遊戲實際流程和邏輯的物件) 說明
欄位名稱 | 資料型態 | 細節 | 內容 |
---|---|---|---|
app | Obj | pomelo_app | 同上 |
pushService | Obj | this.PushService = this.app.get('PushService') | 配置訊息管理器 |
gameLogManager | Obj | this.gameLogManager = this.app.get('GameLogManager') | log物件, 寫賽果進logDb功能 |
tableId | Obj | this.tableId = tableId | 設定桌號 |
lobbyId | Obj | this.lobbyId = lobbyId | 設定大廳Id |
theme | Obj | this.theme = theme | 關卡描述物件 |
gameStatus | int | this.gameStatus = STATUS_WAIT_PLAY | 遊戲狀態 1:遊戲中 2:押注時間 3:新局 4:等待展演轉場表演 |
betTime | int | this.betTime = 300 | 押注時間 |
showTime | int | this.showTime = 3 | 展演時間, 目前三秒 |
maxLaserGun | Obj | this.maxLaserGun = this.theme.Controll_Value.Laser_max_execute_rate | 雷射砲啟動倍數上限 |
laserGunRate | Obj | this.laserGunRate = this.theme.Controll_Value.Laser_pool_ratio | 雷射砲抽取%數 |
poolProb | Obj | this.poolProb = this.theme.Controll_Value.All_pool_rate | 進水量 |
normalPoolRate | Obj | this.normalPoolRate = this.theme.Controll_Value.Normal_pool_rate | 一般水池%數 |
armsPoolRate | Obj | this.armsPoolRate = this.theme.Controll_Value.Weapon_pool_rate | 特殊武器水池%數 |
maxWin | Obj | this.maxWin = this.theme.Controll_Value.Max_win | 最大贏分限制 |
betRange | Obj | this.betRange = this.theme.Bet_Ratio[lobbyId.toString()] | 注碼範圍 |
betHitValue | Obj | this.betHitValue = this.theme.Bet_Hit_Value[lobbyId.toString()] | 注碼對應魚量 |
broadcastList | Obj | self.broadcastList.push(data) | 廣播列表陣列, 分全廳和該桌廣播 |
maxUserNum | int | this.maxUserNum = 4 | 玩家上限, 魚機四人 |
YahuIdList | Array | this.YahuIdList = [] | 同桌玩家ID陣列 |
lockState | bool | this.lockState = false | Table上鎖狀態(若上鎖中,不分配玩家進入此桌)(BOSS出場,海盜船出場都會上鎖) |
leaveLockState | bool | this.leaveLockState = false | 最後一個玩家離開時的上鎖狀態 |
fishIdCount | int | this.fishIdCount = 0 | 魚ID Count計數器 |
gameMode | int | this.gameMode = NORMAL_MODE | 遊戲模式 1:普通 2:陣型 3:海盜船 |
gameNextMode | int | this.gameNextMode = 0 | 下一局遊戲模式 |
gameMap | Obj | this.gameMap = parseInt(Math.random() * this.theme.BG_Control.formation.BG_count + 1) | 遊戲地圖 |
gameNextMap | Obj | this.gameNextMap = 0 | 下一局遊戲地圖 |
gmaeSong | Obj | this.gmaeSong = parseInt(Math.random() * this.theme.BG_Control.formation.BGM_count + 1) | 遊戲音樂 |
gameNextSong | int | this.gameNextSong = 0 | 下一局遊戲音樂 |
gameModeCount | int | this.gameModeCount = 0 | 局數計數 |
bossEndTime | int | this.bossEndTime = 0 | BOSS結束時間 |
bossCome | bool | this.bossCome = false | BOSS出現 |
onRunTimer | Obj | this.onRunTimer = null | 遊戲計時器 |
runCount | int | this.runCount = 1 | timer count |
fishScriptTime | int | this.fishScriptTime = 0 | 出魚腳本timer count的最大值 |
fishScript | Obj | this.fishScript = {} | 出魚腳本 |
fishScriptWithTime | Obj | this.fishScriptWithTime = {} | 出魚腳本ID對應上的時間 |
allDeadFish | Obj | this.allDeadFish = {} | 全部死魚物件 |
bossScript | Obj | this.bossScript = {} | boss腳本物件 |
userPoolMin | Obj | this.userPoolMin = {} | 玩家個人小水池 |
userPoolMax | Obj | this.userPoolMax = {} | 玩家個人大水池 |
userBulletData | Obj | this.userBulletData = {}; | 玩家所有子彈輸贏資訊(統計這發子彈打死什麼魚, 贏分和賠率多少, 最後定時寫入賽果內) |
userBulletDataNow | Obj | this.userBulletDataNow = {} | 玩家目前場上子彈資訊(單純紀錄場上存在的子彈資訊) |
userCatchFishData | Obj | this.userCatchFishData = {} | 玩家打死魚相關資訊 |
GameTable(函式說明)
| 函式名稱 | 細節 | 內容 |
| –––– | –––– | –––– |–––– |
| findTableEmptyPos | function() | 尋找Table空位 |
| findTablePos | function (yahuId) | 尋找玩家在Table中的Pos |
| joinTable | function (gameUser, pos, callback) | 玩家進入Table
1:加入前最後確認是否有上鎖
2:取得小水池
3:取得大水池
4:入桌基本資料初始化
5:PushService.pushByUID Key='USER_JOIN_TABLE'
6:廣播給所有user table中所有user狀況 key='TABLE_USER_INFO' |
| charge | function (pos, amount) | 玩家遊戲內充值 PushService.pushByUID Key='CHARGE'
|
| userLeave | function (pos) | 玩家離開遊戲
1:移除self.YahuIdList陣列
2:設定離開旗標 self.userLeaveFlag[pos] = true
|
| userLeaveHandling | function (pos) | 玩家離開遊戲實際處理
1:檢查中魚狀態, 如果有不允許往下執行
2:檢查寫賽果狀態, 如果有不允許往下執行
3:刪除場上子彈+同步給場上玩家
4:冰凍狀態解除+同步給場上玩家
5:緩速狀態解除+同步給場上玩家
6:寫賽果
7:清除剩餘資料 self.userXXXXX = xxxx
8:PushService 通知玩家離開 key='USER_LEAVE_TABLE'
9:PushService 通知其他玩家哪個位置的玩家離開 key='LEAVE_POS'
|
| playerUseItem | function (pos, itemType, callback) | 玩家使用道具
1:確認gameStatus是否為可以使用道具狀態
2:確認gameMode是否為可以使用道具狀態
3:確認道具是否為可使用狀態,若是處於冰凍狀態所有道具都不可使用
4:緩速要是玩家自己的在啟用中則不能再次使用
5:確認道具數量
6:減去道具數量
7:啟用效果
8:同步給場上玩家 PushService.pushByList key='USE_ITEM'
|
| playerBet | function (pos, fortAngle, betLevel, bulletKind, lockFishId, callback) | 玩家發射子彈 |
| playerCatchFish | function (pos, bulletId, fishIdList, linkFishList, callback) | 玩家中魚處理 |
| runResult | function (pos, useMinPool, bulletIsNull, bulletData, validFishId, validFishDataId, validLinkFishId, validLinkFishDataId, linkageExplosionEnabled, linkFishEnabled, pirateShipEnabled) | 中魚計算 |
| readFishScript | function (fishScriptMode) | 產出出魚腳本 |
| randomRunFish | function (timeCount, kindAppearList) | 產出魚物件 (一般模式) |
| onRunLogic | function (self) | 遊戲計時器 |
| runLogicStart | function() | 啟動計時器 |
| broadcastHandling | function() | 處理廣播訊息 |
| writeRecord | function(pos) | 寫賽果 |
| userPointCompute | function(pos, computePoint, action) | user point 更動 |
| userDisplayPointCompute | function((pos, computePoint, action) | user display point 更動 |
| RandGrpID | function() | 產生局號 |
欄位名稱 | 資料型態 | 內容 |
---|---|---|
tablePosNickName | Obj | 登入會員資料(有四個玩家資料) |
tablePosPoint | Obj | 登入會員點數(有四個玩家資料) |
tableLaserGunValue | Obj | 雷射炮充能狀態轉為百分比(有四個玩家資料) |
{
"tablePosNickName":{
"1":"Leo",
"2":null,
"3":null,
"4":null},
"tablePosPoint":{
"1":998194,
"2":0,
"3":0,
"4":0},
"tableLaserGunValue":{
"1":0,
"2":0,
"3":0,
"4":0}
}
欄位名稱 | 資料型態 | 內容 |
---|---|---|
id | int | 腳本中的ID |
data_id | int | 產魚腳本資料中的Fish_id |
fish_kind | string | 魚種 |
time | int | 魚出現的時間 |
speed | int | 游動速度 |
sub_id | int | 魚群用ID |
path_id | int | 游動路徑Id |
{
"appearFishData":[
{"id":1,"data_id":4,"fish_kind":"F","time":2,"speed":103,"sub_id":0,"path_id":1010},
{"id":2,"data_id":8,"fish_kind":"F","time":2,"speed":114,"sub_id":1,"path_id":1071},
{"id":3,"data_id":8,"fish_kind":"F","time":2,"speed":109,"sub_id":2,"path_id":1071},
{"id":4,"data_id":8,"fish_kind":"F","time":2,"speed":110,"sub_id":3,"path_id":1071},
{"id":5,"data_id":8,"fish_kind":"F","time":2,"speed":115,"sub_id":4,"path_id":1071},
{"id":6,"data_id":8,"fish_kind":"F","time":2,"speed":114,"sub_id":5,"path_id":1071}
]
}
欄位名稱 | 資料型態 | 內容 |
---|---|---|
userPos | int | 座位Id |
bulletId | int | 子彈ID 規則是 yahuId + 流水號 |
fortAngle | int | 子彈角度 |
betLevel | int | 子彈等級 |
userPoint | int | 腳本中的ID |
userLaserGunValue | int | 雷射槍的百分比能量 |
dirllMaxCount | int | 鑽頭炮相關資料(待查) |
lockFishId | int | 待查 |
{
"userPos":"1",
"bulletId":"2-1",
"fortAngle":1.0061878773508814,
"betLevel":1,"bulletKind":1,
"userPoint":999999,
"userLaserGunValue":0,
"dirllMaxCount":0,
"lockFishId":-1
}
欄位名稱 | 資料型態 | 內容 |
---|---|---|
userPos | int | 座位Id |
bulletId | int | 子彈ID 規則是 yahuId + 流水號 |
FishId | int | 打中魚的FishId |
{
"userPos":"1",
"bulletId":"2-1",
"fishId":4
}
欄位名稱 | 資料型態 | 內容 |
---|---|---|
userPos | int | 座位Id |
fishDeadCount | int | 魚死資料的數量 |
fishDeadData | int | 魚死資料 |
userPoint | int | 座位目前的金額 |
{
"userPos":"1",
"fishDeadCount":1,
"fishDeadData":{
"4":{"data_id":8,"fish_kind":"F","win":8,"dead_odds":0,"get_item":false}
},
"userPoint":1000007
}
一秒會送一次, 共五次給Client, nextSec 5~1 的封包
欄位名稱 | 資料型態 | 內容 |
---|---|---|
nextMode | int | 下一關的出魚模式 遊戲模式 1:普通魚 2:陣型魚 3:海盜船 |
nextSec | int | 下一關的秒數 |
{
"nextMode": 1,
"nextSec": 5
}
名稱 | 資料內容 | 內容 |
---|---|---|
STATUS_WAIT_PLAY | 1 | 剛從大廳進入桌時, 會瞬間切到此狀態 |
STATUS_BET_TIME | 2 | 玩家可以押注, 此流程也會按照產魚腳本來產魚 |
STATUS_NEW_GAME | 3 | 另起新局 |
STATUS_WAIT_SHOW | 4 | 展演時間 |
名稱 | 資料內容 | 內容 |
---|---|---|
NORMAL_MODE | 1 | 一般產魚 |
FORMATION_MODE | 2 | 生產魚陣 |
PIRATE_SHIP_MODE | 3 | 生產海盜船 |
其實就是 gameMode, 或是產魚模式 |
/* 產出出魚腳本
* fishScriptMode 是目前的產魚模式
*/
function readFishScript( fishScriptMode ){
this.betTime和, 來決定每一秒要出什麼魚 = 300; // 起始產魚腳本最大秒數
// 根據betTime和theme.json, 來決定每一秒要出什麼魚 (i=偏移秒數)
for (var i = 1; i <= self.betTime; i++) {
// 根據模式來決定目前產魚的方式
switch(fishScriptMode) {
case NORMAL_MODE: // 一般產魚
// 決定出魚邏輯區 (根據 Group_Appear_Data 來決定)
// 使用randomRunFish函式, 塞入該秒數的出魚陣列(randomRunFish)
// 將魚物件塞入腳本結構中
self.fishScript[fishIds[j]] = randomRunFish[fishIds[j]];
// 將fish id塞入產魚時間計數結構中
self.fishScriptWithTime[randomRunFish[fishIds[j]].time].push(fishIds[j]);
// 預製產魚腳本時, 不斷更新該腳本最大時間計數, 方便判斷何時要進入下一局 'STATUS_NEW_GAME'
if (randomRunFish[fishIds[j]].time > self.fishScriptTime)
self.fishScriptTime = randomRunFish[fishIds[j]].time;
break;
case FORMATION_MODE: // 陣型魚模式
break;
case PIRATE_SHIP_MODE: // 海盜船模式
break;
}
}
}
剛進遊戲桌內時, 設定遊戲邏輯計時器, 每秒觸發一次
tableObj.readFishScript(tableObj.gameMode); // 產魚腳本
tableObj.runLogicStart(); // 啟動計時器(1秒觸發一次)
計時器內的 onRunLogic 主流程(簡化)
// step-1 如果有玩家, 就開始遊戲流程的切換
if (self.YahuIdList.length) {
if (self.gameStatus == STATUS_BET_TIME) {
// 一般押注時間
// step-a 執行廣播訊息 (每兩個count才執行一次)
if (self.runCount % 2 == 0)
self.broadcastHandling();
// 檢查道具狀態(如果狀態時間超過,就同步給場上玩家)
if (道具時間超過) {
self.PushService.pushByList(self.YahuIdList, "ITEM_EFFECT_END", msg, utils.doNothing);
}
// 冰凍狀態下停止出魚
if ( isFrozen == false ){
// 丟骰子隨機決定是否要出 'FISH_BOSS' 魚(有符合基本出boss魚條件)
if (如果要出Boss魚) {
// 出BOSS時, 房間會上鎖, 不允許其他玩家進入
self.lockState = true;
self.bossCome = true;
// 塞入BOSS專用腳本
self.bossScript[scriptData.id] = scriptData;
}
// 出魚腳本出魚
var scriptFishIds = self.fishScriptWithTime[self.runCount]; // 取得目前秒數的出魚資料
for (var i = 0; i < scriptFishIds.length; i++) {
// 讀取產魚陣列
var scriptFishId = scriptFishIds[i];
var scriptFishData = self.fishScript[scriptFishId];
// 塞入準備出魚陣列
appearFishData.push(scriptFishData);
}
// 有出普通魚,同步給場上玩家
if (appearFishData.length) {
var msg = {
"appearFishData": appearFishData
}
self.PushService.pushByList(self.YahuIdList, "FISH_APPEAR", msg, utils.doNothing);
}
// 有出BOSS,同步給場上玩家
if (appearBossData.length) {
var msg = {
"appearBossData": appearBossData
}
self.PushService.pushByList(self.YahuIdList, "BOSS_APPEAR", msg, utils.doNothing);
}
// 準備出下一次關卡,
if (self.runCount == self.fishScriptTime) {
// 如果 腳本計時器 == 最後一隻魚的生產時間, 就準備換下一關卡
if (self.gameMode == NORMAL_MODE) {
// 如果目前是一般出魚模式時
if (8局以上有20%機率出海盜船 或 15局以上的話必出海盜船){
// 20%出海盜船
self.gameNextMode = PIRATE_SHIP_MODE;
}else{
// 80%出陣型魚
self.gameNextMode = FORMATION_MODE;
}
}else{
// 一般模式外下一局都會回到一般模式
self.gameNextMode = NORMAL_MODE;
}
// 出魚結束,同步換關訊息
if (self.runCount > self.fishScriptTime) {
self.PushService.pushByList(self.YahuIdList, "READY_NEXT_MODE", msg, utils.doNothing);
}
// 這邊等待5個tick讓client做結束處理
if (self.runCount >= self.fishScriptTime + 5) {
self.gameStatus = STATUS_NEW_GAME; // 切成開新局狀態
}
}
}
self.runCount++; // 腳本計時器累加
} else if (self.gameStatus == STATUS_NEW_GAME) {
// 另啟新局
// 根據桌內人數, 依序寫入賽果跟判斷是否廣播
for (var i = 1; i <= self.maxUserNum; i++) {
// step-a 寫賽果(如果沒有玩家要離開)
self.writeRecord(i);
// step-b 如果上一關是海盜船, 且玩家贏分 > x分, 就廣播
self.broadcastList.push(data);
// 如果是海盜船關卡結束, 進行房間解鎖
self.lockState = false;
}
// step-c 通知切換下一局 self.gameMode = self.gameNextMode
self.PushService.pushByList(self.YahuIdList, "NEXT_MODE", msg, utils.doNothing);
// step-d 強制解除冰凍和緩速狀態, 並且廣播給同桌玩家
self.PushService.pushByList(self.YahuIdList, "ITEM_EFFECT_END", msg, utils.doNothing);
// step-e 讀取下一次的產魚腳本
self.readFishScript(self.gameMode);
// step-f 切成 展演時間 流程
self.gameStatus = STATUS_WAIT_SHOW; // 切成展演狀態
} else if (self.gameStatus == STATUS_WAIT_SHOW) {
// 展演時間 (刷海浪)
self.runCount++; // 腳本計時器累加
// step-a 五秒後切到 STATUS_BET_TIME 流程
self.gameStatus = STATUS_BET_TIME;
}
}
// step-2 如果有玩家的 userLeaveFlag(離開旗標), 就執行userLeaveHandling(離開函式)
for (var i = 1; i <= self.maxUserNum; i++) {
if (self.userLeaveFlag[i]) {
self.userLeaveHandling(i);
}
}
// step-3 此桌都沒玩家時的table初始化
// step-4 重新設定計時器
self.onRunTimer = setTimeout(self.onRunLogic, ON_RUN_TIME, self);
產魚過程
1.Server啟動時, 程式讀取產魚描述檔案 theme.json
2.readFishScript 來決定, 每秒出什麼魚,
出魚資料塞入 self.fishScript 陣列內
出魚時間塞入 self.fishScriptWithTime 陣列內
3.冰凍狀態下不出魚
4.在主流程(onRunLogic) 遊戲狀態是 STATUS_BET_TIME 且 非冰凍狀態下,
會根據 self.fishScript 和 self.fishScriptWithTime 來決定出魚資料
出魚資料塞入 appearFishData 陣列
5.根據 appearFishData 來依序出魚, 並且廣播給Client
self.PushService.pushByList(self.YahuIdList, "FISH_APPEAR", msg, utils.doNothing);
6 FISH_BOSS魚有點特例, 在主流程中再另行隨機判斷
塞入 self.bossScript 陣列內
// Boss預告
self.PushService.pushByList(self.YahuIdList, "BOSS_COME", {"fishDataId": fishDataId}, utils.doNothing);
// 生產Boss
self.PushService.pushByList(self.YahuIdList, "BOSS_APPEAR", msg, utils.doNothing);
魚種名稱 | 資料內容 | 內容 |
---|---|---|
FISH_BOSS | A | BOSS魚 |
FISH_SMALL_BOSS | B | 小BOSS魚 |
FISH_BIG | C | 大型魚(含組合魚) |
FISH_ODDS | D | 倍率魚 |
FISH_MIDDLE | E | 中型魚 |
FISH_SMALL | F | 小型魚 |
FISH_SPECIAL | G | 特殊魚種(道具魚) |
FISH_PIRATE_SHIP | S | 海盜船 |
玩家中魚處理
/*
* @param {Number} pos 玩家座位Id
* @param {String} bulletId 子彈ID
* @param {Array} fishIdList 打中的魚的列表 (魚的流水ID)
* @param {Array} linkFishList 待查
* @param {function} callback 有錯誤時的 ErrorCallBackFunction
*/
GameTable.prototype.playerCatchFish = function (pos, bulletId, fishIdList, linkFishList, callback) {
// 移除重複的fish id
// 取出第一隻魚的data id
// 檢查是否為有效子彈
bulletData = self.userBulletDataNow[pos][bulletId];
if (bulletData) {
// 有效子彈
// 確認使用的水池(根據押注等級數值來判斷使用大小水池)
if (betLevel > BET_5)
useMinPool = false;
// 子彈命中廣播
var msg = {
"userPos": pos,
"bulletId": bulletId,
"fishId": fishIdList[0],
};
self.PushService.pushByList(self.YahuIdList, "BULLET_HIT_FISH", msg, utils.doNothing);
if (如果是鑽頭炮) {
// 水池進水(有分大小水池,只貼局部)
// 小水池
// 一般水池進水公式
self.userPoolMin[pos].normal_values += (betPoint * self.poolProb / 100) * self.normalPoolRate / 100;
// 武器水池進水公式
self.userPoolMin[pos].arms_values += (betPoint * self.poolProb / 100) * self.armsPoolRate / 100;
}
.... 省略
}
// 針對無效子彈清除結構
// 針對抓取魚資料做處理(製作有效魚陣列 validFishId 和 validFishDataId)
for (var i = 0; i < fishIdList.length; i++) {
.... 省略
// 加入有效魚中
validFishId.push(fishScriptData.id);
validFishDataId.push(fishScriptData.data_id);
}
.... 省略
// 進行 ********* 中魚計算 *********
var result = self.runResult( ... )
// 鑽頭砲相關處理
if ( 如果是鑽頭炮 ){
// 武器贏分累加
self.drillStatus[pos].win += result.armsWin;
// 武器水池扣水
if (useMinPool)
self.userPoolMin[pos].arms_values -= result.armsWin;
else
self.userPoolMax[pos].arms_values -= result.armsWin;
// 鑽頭砲結束
if (self.drillStatus[pos].count == self.drillStatus[pos].max) {
// 製作廣播結構
var data = {
"broadcastId": DRILL_WIN_POINT,
"msgData": {
"user_id": "******" + self.userUserIds[pos].slice(self.userUserIds[pos].length - 3),
"win": self.drillStatus[pos].win
}
}
self.broadcastList.push(data);
}
}
// 連環炸彈相關處理
if (self.linkageExplosionStatus[pos][bulletId]) {
// 武器贏分累加
self.linkageExplosionStatus[pos][bulletId].win += result.armsWin;
// 武器水池扣水
if (useMinPool)
self.userPoolMin[pos].arms_values -= result.armsWin;
else
self.userPoolMax[pos].arms_values -= result.armsWin;
// 連環炸彈結束
if( 目前的count == 最大的max ){
// 製作廣播結構
var data = {
"broadcastId": CHAIN_BOMB_WIN_POINT,
"msgData": {
"user_id": "******" + self.userUserIds[pos].slice(self.userUserIds[pos].length - 3),
"win": self.linkageExplosionStatus[pos][bulletId].win
}
}
self.broadcastList.push(data);
}
}
// 沒死魚不送封包
// 雷射砲廣播處理
// 針對死魚做處理
for (var i = 0; i < deadFishIds.length; i++) {
// 製作死魚資料
var data = {
"bullet_id": bulletId,
"bet_point": betPoint,
"bullet_kind": bulletKind,
"data_id": result.fishDeadData[deadFishIds[i]].data_id,
"fish_kind": result.fishDeadData[deadFishIds[i]].fish_kind,
"win": result.fishDeadData[deadFishIds[i]].win,
"kill_pos": pos
}
// 記錄整桌死魚物件排除海盜船
// 記錄玩家個人打死魚
self.userCatchFishData[pos][deadFishIds[i]] = data;
// 道具處理
if (result.fishDeadData[deadFishIds[i]].get_item) {
isGetItem = true; // 獲得道具旗標
if (data.data_id == SP_FROZEN) {
//SP_FROZEN 最多使用次數 = 3
self.userItemNum[pos][FROZEN]++;
} else if (data.data_id == SP_SLOW) {
//SLOW 最多使用次數 =3
self.userItemNum[pos][SLOW]++;
} else if (data.data_id == SP_DRILL) {
//DRILL 最多使用次數 = 1
self.userItemNum[pos][DRILL]++;
}
}
// 打死small boss
var msg = {
"broadcastId": SMALL_BOSS_DEAD,
"msgData": {
"user_id": "******" + self.userUserIds[pos].slice(self.userUserIds[pos].length - 3),
"fish_id": data.data_id,
"win": result.allWin + result.armsWin
}
}
self.broadcastList.push(msg);
// 打中海盜船
if (data.fish_kind == FISH_PIRATE_SHIP) {
// 武器水池扣水
if (useMinPool)
self.userPoolMin[pos].arms_values -= result.armsWin;
else
self.userPoolMax[pos].arms_values -= result.armsWin;
}
// 打死水雷
if (data.data_id == SP_PARTIAL_BOMB) {
var msg = {
"broadcastId": PARTIAL_BOMB_WIN_POINT,
"msgData": {
"user_id": "******" + self.userUserIds[pos].slice(self.userUserIds[pos].length - 3),
"win": result.armsWin
}
}
self.broadcastList.push(msg);
}
// 打死魚雷
if (data.data_id == SP_FULL_SCREEN_BOMB) {
var msg = {
"broadcastId": PARTIAL_BOMB_WIN_POINT,
"msgData": {
"user_id": "******" + self.userUserIds[pos].slice(self.userUserIds[pos].length - 3),
"win": result.armsWin
}
}
self.broadcastList.push(msg);
// 武器水池扣水
if (useMinPool)
self.userPoolMin[pos].arms_values -= result.armsWin;
else
self.userPoolMax[pos].arms_values -= result.armsWin;
}
// 打死BOSS
if (data.fish_kind == FISH_BOSS) {
// 清除房間鎖狀態
self.lockState = false;
// BOSS來襲結束,同步給場上玩家
self.PushService.pushByList(self.YahuIdList, "BOSS_COME_END", {}, utils.doNothing);
if (一定贏分以上進行廣播){
var msg = {
"broadcastId": BOSS_DEAD,
"msgData": {
"user_id": "******" + self.userUserIds[pos].slice(self.userUserIds[pos].length - 3),
"fish_id": data.data_id,
"win": result.allWin
}
}
self.broadcastList.push(msg);
}
}
// 打死連環炸彈
if (data.data_id == SP_CHAIN_BOMB) {
... 省略
// 同步給場上玩家
var msg = {
"userPos": pos,
"bulletId": bulletId,
"fishId": parseInt(deadFishIds[i]),
"explosionCount": explosionCount,
"explosionX": explosionX,
"explosionY": explosionY
};
self.PushService.pushByList(self.YahuIdList, "CHAIN_BOMB_DATA", msg, utils.doNothing);
}
// 有取得道具,同步玩家自己的道具狀態
if (isGetItem) {
var msg = {
"userItemNum": self.userItemNum[pos]
};
self.PushService.pushByUID(self.userYahuIds[pos], "USER_ITEM_NUM", msg, utils.doNothing);
}
}
// 贏分加入本金
self.userPointCompute(pos, result.allWin + result.armsWin, ADD);
self.userDisplayPointCompute(pos, result.allWin + result.armsWin, ADD);
// 總贏分
self.userTotalWin[pos] += result.allWin + result.armsWin;
// 將總倍率以及總贏分放入有效子彈資料
self.userBulletData[pos][bulletId].win += result.allWin + result.armsWin;
self.userBulletData[pos][bulletId].total_odds += result.totalOdds;
// 死魚同步給場上玩家
var msg = {
"userPos": pos,
"fishDeadCount": deadFishIds.length,
"fishDeadData": result.fishDeadData,
"userPoint": self.userDisplayPoint[pos],
};
self.PushService.pushByList(self.YahuIdList, "CATCH_FISH", msg, function(err,data) {
return utils.invokeCallback(callback, null, null);
});
}
備註1: 水池進水公式請參考水庫進水公式
中魚計算
/*
* @param {int} pos 玩家座位Id
* @param {Number} useMinPool 玩家小水池
* @param {bool} bulletIsNull 是否為無效子彈旗標
* @param {Object} bulletData 子彈資料
* @param {Number} validFishId 這一發子彈打中的..所有魚的流水Id(有效魚)
* @param {Number} validFishDataId 這一發子彈打中的..所有魚的Fish_id(有效魚)
* @param {Number} validLinkFishId 待補
* @param {Number} validLinkFishDataId 待補
* @param {Number} linkageExplosionEnabled 待補
* @param {Number} linkFishEnabled 待補
* @param {Number} pirateShipEnabled 待補 打到海盜船旗標
* @return {Object} result 回傳此次擊殺的結果
*/
GameTable.prototype.runResult = function( ... 省略 ){
// 無效子彈,直接送回無效狀況
// 整理所有魚的賠率
for (var i = 0; i < validFishDataId.length; i++) {
fishRatio[validLinkFishDataId[i]] = parseInt(Math.random() * self.theme.Fish_Data[validLinkFishDataId[i]].Ratio_Random + self.theme.Fish_Data[validLinkFishDataId[i]].Bet_ratio);
}
// 取出水池值 & 水池修正相關資料
if (useMinPool) {
// 使用小水池
poolValues = self.userPoolMin[pos].normal_values;
poolArmsValues = self.userPoolMin[pos].arms_values;
if (poolValues > 0) {
// (大魚及小魚)(正向)
normalBigContainer = self.theme.Normal_Fish_Pool.Positive.small.big_fish.container;
normalSmallContainer = self.theme.Normal_Fish_Pool.Positive.small.small_fish.container;
// 依人數取得修正資料(分 3人同桌 4人同桌, 取不同的RatioFix )
... 省略
// 如果是兩人取得 【正向】 普通子彈對應的RatioFix
normalBigRatioFix = self.theme.Normal_Fish_Pool.Positive.small.big_fish.ratio_2; // 兩人修正-大魚
normalSmallRatioFix = self.theme.Normal_Fish_Pool.Positive.small.small_fish.ratio_2; // 兩人修正-小魚
}else{
// (大魚及小魚)(負向)
... 省略
// 如果是兩人取得 【負向】 普通子彈對應的RatioFix
normalBigRatioFix = self.theme.Normal_Fish_Pool.Negative.small.big_fish.ratio_2;
normalSmallRatioFix = self.theme.Normal_Fish_Pool.Negative.small.small_fish.ratio_2;
}
// 武器水池修正資料
// 判斷水位
spContainer = self.theme.Special_Fish_Pool.Positive.small.container;
// 如果是兩人取得 【正向】 武器對應的RatioFix
spRatioFix = self.theme.Special_Fish_Pool.Positive.small.ratio_2;
}else{
// 使用大水池
poolValues = self.userPoolMax[pos].normal_values;
poolArmsValues = self.userPoolMax[pos].arms_values;
// 一般水池修正資料 (正水池/負水池)
if (poolValues > 0) {
// 判斷水位 (大魚及小魚)(正向)
normalBigContainer = self.theme.Normal_Fish_Pool.Positive.big.big_fish.container;
normalSmallContainer = self.theme.Normal_Fish_Pool.Positive.big.small_fish.container;
// 依人數取得修正資料(分 3人同桌 4人同桌, 取不同的RatioFix )
... 省略
// 如果是兩人取得 【正向】 對應的RatioFix
normalBigRatioFix = self.theme.Normal_Fish_Pool.Positive.big.big_fish.ratio_2;
normalSmallRatioFix = self.theme.Normal_Fish_Pool.Positive.big.small_fish.ratio_2;
}else{
// 判斷水位 (大魚及小魚)(負向)
normalBigContainer = self.theme.Normal_Fish_Pool.Negative.big.big_fish.container;
normalSmallContainer = self.theme.Normal_Fish_Pool.Negative.big.small_fish.container;
... 省略 同上
}
// 武器水池修正資料
// 判斷水位
... 省略
// 依人數取得修正資料
spRatioFix = self.theme.Special_Fish_Pool.Positive.big.ratio_2;
}
// 計算擊中海盜船(待補)
if (如果打中海盜船) {
var pirateShipWeight = self.theme.Pirate_Ship.Random_Gap; // ????
for (var i = 0; i < pirateShipWeight.length; i++) {
if (randWeight < pirateShipWeight[i]) {
fishRatio[FISH_PIRATE_SHIP_DATA_ID] = self.theme.Pirate_Ship.Reward_Ratio[i];
break;
}
}
}
// 確認大小水池取出MAX BET
maxBet = self.userPoolXXX[pos].max_bet;
// 整理所有魚的致死
for (var i = 0; i < validFishDataId.length; i++) {
// 取出魚的資料
var fishData = self.theme.Fish_Data[validFishDataId[i]];
// 資料不存在,放入初始致死率
if(!fishKillRate[validFishDataId[i]]){
fishKillRate[validFishDataId[i]] = fishData.Kill_rate; // 資料不存在,放入初始致死率
}
// 不同魚使用不同水池
if(fishData.Fish_group == FISH_SMALL || fishData.Fish_group == FISH_MIDDLE) {
// 小型魚 & 中型魚使用小魚一般水池修正資料
for (var j = 0; j < normalSmallContainer.length; j++) {
if (isTrue) {
if (poolValues > 0)
fishKillRate[validFishDataId[i]] = parseInt(fishKillRate[validFishDataId[i]] * normalSmallRatioFix[j] / 10);
else
fishKillRate[validFishDataId[i]] = parseInt(fishKillRate[validFishDataId[i]] * 10 / normalSmallRatioFix[j]); // 水庫下降, 代表玩家輸了, 開始調高致死率
}
}
} else if (fishData.Fish_group != FISH_PIRATE_SHIP && fishData.Fish_id != SP_DRILL && fishData.Fish_id != SP_CHAIN_BOMB && fishData.Fish_id != SP_FULL_SCREEN_BOMB && fishData.Fish_id != SP_PARTIAL_BOMB) {
// 大型魚使用大於水池修正資料 (除去海盜船/鑽頭砲/連環/全屏/區域)
} else {
}
}
.... 待補....
}
for (var i = 0; i < validFishDataId.length; i++) {
// 原本的致死率 / 魚網捕的魚隻數
fishKillRate[validFishDataId[i]] = parseInt( fishKillRate[validFishDataId[i]] / validFishDataId.length );
}
假設有一隻魚
odd = 5
RTP = 98%
魚原始死機率 = 19.6% 的機率會死
如果一次捕獲兩隻
所以第一隻魚的死亡率 => 19.6 /2 = 9.8%
所以第二隻魚的死亡率 => 19.6 /2 = 9.8%
期望值為 9.8 + 9.8 = 19.6
如果一次捕獲三隻
所以第一隻魚的死亡率 => 19.6 /3 = 6.5%
所以第二隻魚的死亡率 => 19.6 /3 = 6.5%
所以第二隻魚的死亡率 => 19.6 /3 = 6.5%
期望值為 6.5 + 6.5 + 6.5 = 19.6
如果捕獲不同賠率的魚 Odd=5 Odd=20
A魚原始死機率 = 19.6% B魚原始死的機率 = 4.9%
如果一次捕獲兩隻
所以第一隻魚的死亡率 => 19.6 /2 = 9.8%
所以第二隻魚的死亡率 => 4.9 /2 = 2.45%
期望值為 9.8 + 2.45 = 12.25
theme.json 內, Controll_Value 裡面的控制變數
名稱 | 數值 | 內容 |
---|---|---|
All_pool_rate | 96 | 進水量 = All_pool_rate = self.poolProb |
Normal_pool_rate | 85 | 普通子彈水池%數, 控制 normal_values |
Weapon_pool_rate | 15 | 特殊武器水池%數, 控制 arms_values |
Normal_Fish_Pool( 根據個人水池的正跟負, 取得不同的修正值, 此值會影響修正魚的致死率 )
名稱 | 數值 | 內容 |
---|
Normal_Fish_Pool( 根據個人水池的正跟負, 取得不同的修正值, 此值會影響修正魚的致死率 )
名稱 | 數值 | 內容 |
---|---|---|
spRatioFix | [10,10,12,15,20] | 特殊武器致死率修正, 有分 2人3人4人的修正值 |
水庫和機率的規則
1.規則1 theme.json 裡面的 Fish_Data 的 Kill_rate(致死率) 我發現好像偏低 都是 已 rtp 0.75 去算的 ( 例如 綠魚 條紋魚 大眼魚.... 美人魚)
2.規則2 if 個人水池 > 0 {
// 個人水池大於零, 代表玩家輸錢, 所以增加致死率, 方便玩家打死魚
原本致死率 = 原本致死率 * (正向小水池修正因子 / 10)
}else{
// 個人水池小於零, 代表玩家贏錢, 所以縮小致死率, 讓玩家很難打死魚
原本致死率 = 原本致死率 * (10 / 負向小水池修正因子)
}
3.規則3 一網捕多魚修正 將 【原本魚的致死率 / 捕獲的魚數量 】 去做衰減
4.致死率超過80%一律以80%計
// 一般水池
self.userPoolMin[pos].normal_values += (betPoint * self.poolProb / 100) * self.normalPoolRate / 100;
// 武器水池
self.userPoolMin[pos].arms_values += (betPoint * self.poolProb / 100) * self.armsPoolRate / 100;
假設 子彈一發是1點 (Bet=1)
一般水池進水量 = 1 * 96/100 * 85/100 = 1 * 0.96 * 0.85 = 0.816
武器水池進水量 = 1 * 96/100 * 15/100 = 1 * 0.96 * 0.15 = 0.144
// 扣除個人一般水池 allWin是此魚的總贏分
if (useMinPool)
self.userPoolMin[pos].normal_values -= allWin;
else
self.userPoolMax[pos].normal_values -= allWin;
armsWin = 武器總贏分
allWin = 一般魚總贏分
在判斷是有效子彈後, 先根據 水庫進水公式, 把值灌進大小水池和武器水池內
a.self.userPoolMin[pos].normal_values += (betPoint * self.poolProb / 100) * self.normalPoolRate / 100; // 個人水池
b.self.userPoolMin[pos].arms_values += (betPoint * self.poolProb / 100) * self.armsPoolRate / 100; // 武器水池
呼叫 runResult, 進行中魚計算
a. 根據目前水庫, 取得相對應的機率修正值 (normalBigRatioFix, normalSmallRatioFix)
b. 整理所有魚的初步致死機率 fishKillRate[validFishDataId[i]] = fishData.Kill_rate
c. 根據水庫水位調整魚的致死率, 水庫有水時 fishKillRate[validFishDataId[i]] = parseInt(fishKillRate[validFishDataId[i]] * normalSmallRatioFix[j] / 10);
d. 根據水庫水位調整魚的致死率, 水庫沒水時 fishKillRate[validFishDataId[i]] = parseInt(fishKillRate[validFishDataId[i]] * 10 / normalSmallRatioFix[j]);
e. 道具的致死率跟水庫修正 fishKillRate[validFishDataId[i]] = parseInt(fishKillRate[validFishDataId[i]] * spRatioFix[j] / 10);
e. 跑 cnt < 2 迴圈, for (var cnt = 0; cnt < 2; cnt++)
a. 一網捕多魚的致死率修正, 該魚致死率/捕獲數, 平均分散在每隻捕獲魚 fishKillRate[validFishDataId[i]] = parseInt( fishKillRate[validFishDataId[i]] / validFishDataId.length );
b1. 執行runResult時, 武器水池修正
c. 執行runResult時, 最後算出allWin時, 再從個人水池內 扣回去 self.userPoolMin[pos].normal_values -= allWin;
6. 離開runResult後, 判斷如果是鑽頭炮射擊, 進行武器大小水池修正 self.userPoolMin[pos].arms_values -= result.armsWin;
7. 離開runResult後, 判斷如果是連環炸彈, 進行武器大小水池修正 self.userPoolMin[pos].arms_values -= result.armsWin;
8. 離開runResult後, 檢查死魚陣列, 判斷如果打死海盜船, 進行武器大小水池修正 self.userPoolMin[pos].arms_values -= result.armsWin;
9. 離開runResult後, 檢查死魚陣列, 判斷如果打死魚雷, 進行武器大小水池修正 self.userPoolMin[pos].arms_values -= result.armsWin;
水庫的精華是
1.打魚沒死, 水庫水位上升
2.打死魚, 水庫慢慢下降
水庫水位影響 魚的致死率
根據水庫水位, 取得不同的參數來調整 致死率 (有分 大魚, 小魚, 遊戲桌內[2,3,4]人 )
當 (武器水位 - 武器的贏分) < 0, 變成負值時, 發不出錢時, 會 armOver = true
此時會重跑一次 致死率判斷魚死不死的迴圈, 然後將調整致死率=0, 就讓他再重跑一次流程
3.道具打中的每一隻魚, 致死率=0
4.炸彈類型/海盜船 都打不死, 每一隻魚, 致死率=0
基本上武器水池發不出獎就是直接不發武器水池的獎
gamelist 遊戲列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
game_id | VARCHAR(10) | 主Key, 遊戲Id |
cn_name | VARCHAR(64) | 簡中名稱 |
en_name | VARCHAR(64) | 英文名稱 |
enable | TINYINT(1) | 是否啟用 |
game_category_id | TINYINT(3) | 遊戲類型ID |
chost | VARCHAR(64) | 前端服務器IP |
cport | INT(4) | 前端服務器PORT |
th_name | VARCHAR(64) | 泰文名稱 |
tw_name | VARCHAR(64) | 繁中名稱 |
item 玩家持有道具列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
id | VARCHAR(10) | 主Key, Id, auto_increment 自動增長 |
count | DECIMAL(18,2) | 玩家遊戲點數(float) |
item_id | TINYINT(3) | 道具ID |
user_id | VARCHAR(20) | 玩家ID |
play_log 玩遊戲下注紀錄列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
id | VARCHAR(255) | 流水號 |
agent_id | VARCHAR(255) | 代理商AGENT ID |
app_id | VARCHAR(10) | 遊戲ID |
bet_amount | DECIMAL(18,2) | 注額 |
bet_num | SMALLINT(5) | 投注倍數 |
bonus | DECIMAL(18,2) | 附加遊戲彩金 |
cost | DECIMAL(18,2) | 總投注 |
contribution | DECIMAL(18,6) | 貢獻金 |
payoff | DECIMAL(18,2) | 總損益 |
free_count | SMALLINT(5) | 剩餘免費遊戲次數 |
game_category_id | TINYINT(3) | 遊戲類型 |
game_id | BIGINT(20) | 遊戲ID |
game_table_idx | TINYINT(3) | 使用樣本庫ID |
result_type | TINYINT(3) | 樣本結果 |
jackpot_score | DECIMAL(18,2) | JP彩金 |
linked_score | DECIMAL(18,2) | 連線彩金 |
payoff_time | TIMESTAMP | 派彩時間 |
status | TINYINT(3) | 注單狀態 |
total_score | DECIMAL(18,2) | 總贏分 |
user_id | VARCHAR(20) | 投注玩家ID |
wagers_time | TIMESTAMP | 投注時間 |
date | DATE | 投注日期 |
coefficient | SMALLINT(5) | 得分倍數 |
transactionlog 玩家點數交易紀錄(平台)列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
id | VARCHAR(255) | 交易序號 |
transfer_type | TINYINT(3) | 交易類型 |
user_id | VARCHAR(20) | 交易使用者ID |
amount | DECIMAL(18,2) | 交易金額 |
bef_money | DECIMAL(18,2) | 交易前金額 |
aft_money | DECIMAL(18,2) | 交易後金額 |
create_date | TIMESTAMP | 交易時間 |
status | TINYINT(3) | 交易狀態 |
transactionlog 玩家點數交易紀錄(平台)列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
id | VARCHAR(20) | 玩家ID |
yahu_id | INT(11) | 亞虎玩家ID |
agent | VARCHAR(20) | 代理商AGENT ID |
enabled | TINYINT(1) | 帳號是否啟用 |
last_login | DATETIME | 最後登錄時間 |
last_quit | DATETIME | 最後離線時間 |
ip | VARCHAR(15) | 最後登錄IP |
currency | VARCHAR(5) | 幣別 |
update_date_time | TIMESTAMP | 更新時間 |
create_date_time | TIMESTAMP | 帳號建立時間 |
entergamelog 進入遊戲紀錄列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
user_id | VARCHAR(20) | 玩家ID |
lobby | TINYINT(3) | 進入的大廳Id |
create_date_time | TIMESTAMP | 玩家ID |
game_play_log 遊戲下注紀錄列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
id | VARCHAR(255) | 流水號 |
agent_id | VARCHAR(255) | 代理商AGENT ID |
app_id | VARCHAR(10) | 遊戲ID |
bet_amount | DECIMAL(18,2) | 注額 |
bet_num | SMALLINT(5) | 投注倍數 |
bonus | DECIMAL(18,2) | 附加遊戲彩金 |
cost | DECIMAL(18,2) | 總投注 |
contribution | DECIMAL(18,6) | 貢獻金 |
payoff | DECIMAL(18,2) | 總損益 |
game_category_id | TINYINT(3) | 遊戲類型 |
fish_data_id | TINYINT(3) | 擊中魚ID |
jackpot_score | DECIMAL(18,2) | JP彩金 |
linked_score | DECIMAL(18,2) | 連線彩金 |
payoff_time | TIMESTAMP | 派彩時間 |
status | TINYINT(3) | 注單狀態 |
total_score | DECIMAL(18,2) | 總贏分 |
user_id | VARCHAR(20) | 投注玩家ID |
wagers_time | TIMESTAMP | 投注時間 |
grp_id | VARCHAR(16) | 局號ID |
date | DATE | 投注日期 |
coefficient | SMALLINT(5) | 得分倍數 |
gamepool 遊戲水池數據列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
user_id | VARCHAR(20) | 玩家ID |
max_bet | BIGINT(20) | 水池 Max Bet |
arms_values | DECIMAL(18,2) | 特殊武器水池值 |
normal_values | DECIMAL(18,2) | 一般水池值 |
gameuser 遊戲玩家數據列表
欄位名稱 | 資料型態 | 說明 |
---|---|---|
id | VARCHAR(20) | 玩家ID |
total_bet_amount | VARCHAR(20) | 總投注 |
total_payoff | VARCHAR(20) | 總收穫 |
enabled | VARCHAR(20) | 啟用 |
update_date_time | TIMESTAMP | |
create_date_time | TIMESTAMP |