Integrated basic holepunching for multiplayer codebase.

This commit is contained in:
genxium 2023-01-25 18:26:13 +08:00
parent 5df545e168
commit 8536521136
16 changed files with 1522 additions and 981 deletions

View File

@ -37,6 +37,8 @@ type mysqlConf struct {
type sioConf struct { type sioConf struct {
HostAndPort string `json:"hostAndPort"` HostAndPort string `json:"hostAndPort"`
UdpHost string `json:"udpHost"`
UdpPort int `json:"udpPort"`
} }
type botServerConf struct { type botServerConf struct {

View File

@ -1,3 +1,5 @@
{ {
"hostAndPort": "0.0.0.0:9992" "hostAndPort": "0.0.0.0:9992",
"udpHost": "0.0.0.0",
"udpPort": 3000
} }

View File

@ -23,6 +23,8 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/robfig/cron" "github.com/robfig/cron"
"go.uber.org/zap" "go.uber.org/zap"
"net"
) )
func main() { func main() {
@ -34,7 +36,7 @@ func main() {
env_tools.MergeTestPlayerAccounts() env_tools.MergeTestPlayerAccounts()
} }
models.InitRoomHeapManager() models.InitRoomHeapManager()
startScheduler() // startScheduler()
router := gin.Default() router := gin.Default()
setRouter(router) setRouter(router)
@ -54,6 +56,7 @@ func main() {
} }
Logger.Info("Listening and serving HTTP on", zap.Any("Conf.Sio.HostAndPort", Conf.Sio.HostAndPort)) Logger.Info("Listening and serving HTTP on", zap.Any("Conf.Sio.HostAndPort", Conf.Sio.HostAndPort))
}() }()
go startUdpServer()
var gracefulStop = make(chan os.Signal) var gracefulStop = make(chan os.Signal)
signal.Notify(gracefulStop, syscall.SIGTERM) signal.Notify(gracefulStop, syscall.SIGTERM)
signal.Notify(gracefulStop, syscall.SIGINT) signal.Notify(gracefulStop, syscall.SIGINT)
@ -114,3 +117,26 @@ func startScheduler() {
//c.AddFunc("*/1 * * * * *", FuncName) //c.AddFunc("*/1 * * * * *", FuncName)
c.Start() c.Start()
} }
func startUdpServer() {
conn, err := net.ListenUDP("udp", &net.UDPAddr{
Port: Conf.Sio.UdpPort,
IP: net.ParseIP(Conf.Sio.UdpHost),
})
if err != nil {
panic(err)
}
defer conn.Close()
Logger.Info(fmt.Sprintf("Udp server started at %s", conn.LocalAddr().String()))
for {
message := make([]byte, 2046)
rlen, remote, err := conn.ReadFromUDP(message[:])
if err != nil {
panic(err)
}
Logger.Info(fmt.Sprintf("received: %d bytes from %s\n", rlen, remote))
ws.HandleUdpHolePunchingForPlayer(message[0:rlen], remote)
}
}

View File

@ -50,6 +50,8 @@ type Player struct {
LastSentInputFrameId int32 LastSentInputFrameId int32
AckingFrameId int32 AckingFrameId int32
AckingInputFrameId int32 AckingInputFrameId int32
UdpAddr *PeerUdpAddr
} }
func ExistPlayerByName(name string) (bool, error) { func ExistPlayerByName(name string) (bool, error) {

View File

@ -13,6 +13,7 @@ import (
"io/ioutil" "io/ioutil"
"jsexport/battle" "jsexport/battle"
"math/rand" "math/rand"
"net"
"os" "os"
"path/filepath" "path/filepath"
"resolv" "resolv"
@ -32,6 +33,7 @@ const (
DOWNSYNC_MSG_ACT_BATTLE_STOPPED = int32(3) DOWNSYNC_MSG_ACT_BATTLE_STOPPED = int32(3)
DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4) DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4)
DOWNSYNC_MSG_ACT_PEER_INPUT_BATCH = int32(5) DOWNSYNC_MSG_ACT_PEER_INPUT_BATCH = int32(5)
DOWNSYNC_MSG_ACT_PEER_UDP_ADDR = int32(6)
DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START = int32(-1) DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START = int32(-1)
DOWNSYNC_MSG_ACT_BATTLE_START = int32(0) DOWNSYNC_MSG_ACT_BATTLE_START = int32(0)
@ -176,6 +178,7 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke
defer pR.onPlayerAdded(playerId) defer pR.onPlayerAdded(playerId)
pPlayerFromDbInit.UdpAddr = nil
pPlayerFromDbInit.AckingFrameId = -1 pPlayerFromDbInit.AckingFrameId = -1
pPlayerFromDbInit.AckingInputFrameId = -1 pPlayerFromDbInit.AckingInputFrameId = -1
pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED
@ -215,6 +218,7 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso
*/ */
defer pR.onPlayerReAdded(playerId) defer pR.onPlayerReAdded(playerId)
pEffectiveInRoomPlayerInstance := pR.Players[playerId] pEffectiveInRoomPlayerInstance := pR.Players[playerId]
pEffectiveInRoomPlayerInstance.UdpAddr = nil
pEffectiveInRoomPlayerInstance.AckingFrameId = -1 pEffectiveInRoomPlayerInstance.AckingFrameId = -1
pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1 pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1
pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED
@ -1068,7 +1072,9 @@ func (pR *Room) sendSafely(roomDownsyncFrame *pb.RoomDownsyncFrame, toSendInputF
panic(fmt.Sprintf("Error marshaling downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount)) panic(fmt.Sprintf("Error marshaling downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
} }
if MAGIC_JOIN_INDEX_DEFAULT == peerJoinIndex { shouldUseSecondaryWsSession := (MAGIC_JOIN_INDEX_DEFAULT != peerJoinIndex && DOWNSYNC_MSG_ACT_INPUT_BATCH == act) // FIXME: Simplify the condition
//Logger.Info(fmt.Sprintf("shouldUseSecondaryWsSession=%v: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", shouldUseSecondaryWsSession, pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
if !shouldUseSecondaryWsSession {
if playerDownsyncSession, existent := pR.PlayerDownsyncSessionDict[playerId]; existent { if playerDownsyncSession, existent := pR.PlayerDownsyncSessionDict[playerId]; existent {
if err := playerDownsyncSession.WriteMessage(websocket.BinaryMessage, theBytes); nil != err { if err := playerDownsyncSession.WriteMessage(websocket.BinaryMessage, theBytes); nil != err {
panic(fmt.Sprintf("Error sending primary downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v, err=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount, err)) panic(fmt.Sprintf("Error sending primary downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v, err=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount, err))
@ -1607,3 +1613,53 @@ func (pR *Room) SetSecondarySession(playerId int32, session *websocket.Conn, sig
} }
} }
} }
func (pR *Room) UpdatePeerUdpAddrList(playerId int32, peerAddr *net.UDPAddr, pReq *pb.HolePunchUpsync) {
// TODO: There's a chance that by now "player.JoinIndex" is not yet determined, use a lock to sync
if player, ok := pR.Players[playerId]; ok && MAGIC_JOIN_INDEX_DEFAULT != player.JoinIndex {
playerBattleState := atomic.LoadInt32(&(player.BattleState))
switch playerBattleState {
case PlayerBattleStateIns.DISCONNECTED, PlayerBattleStateIns.LOST, PlayerBattleStateIns.EXPELLED_DURING_GAME, PlayerBattleStateIns.EXPELLED_IN_DISMISSAL:
// Kindly note that "PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK, PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK" are allowed
return
}
if _, existent := pR.PlayerDownsyncSessionDict[playerId]; existent {
player.UdpAddr = &pb.PeerUdpAddr{
Ip: peerAddr.IP.String(),
Port: int32(peerAddr.Port),
AuthKey: pReq.AuthKey,
}
Logger.Info(fmt.Sprintf("UpdatePeerUdpAddrList done for roomId=%v, playerId=%d, peerAddr=%s", pR.Id, playerId, peerAddr))
peerJoinIndex := player.JoinIndex
peerUdpAddrList := make([]*pb.PeerUdpAddr, pR.Capacity, pR.Capacity)
for _, otherPlayer := range pR.Players {
if MAGIC_JOIN_INDEX_DEFAULT == otherPlayer.JoinIndex {
// TODO: Again this shouldn't happen, apply proper locking
continue
}
// In case of highly concurrent update that might occur while later marshalling, use the ptr of a copy
peerUdpAddrList[otherPlayer.JoinIndex-1] = &pb.PeerUdpAddr{
Ip: otherPlayer.UdpAddr.Ip,
Port: otherPlayer.UdpAddr.Port,
AuthKey: otherPlayer.UdpAddr.AuthKey,
}
}
// Broadcast this new UDP addr to all the existing players
for otherPlayerId, otherPlayer := range pR.Players {
otherPlayerBattleState := atomic.LoadInt32(&(otherPlayer.BattleState))
switch otherPlayerBattleState {
case PlayerBattleStateIns.DISCONNECTED, PlayerBattleStateIns.LOST, PlayerBattleStateIns.EXPELLED_DURING_GAME, PlayerBattleStateIns.EXPELLED_IN_DISMISSAL:
continue
}
Logger.Info(fmt.Sprintf("Downsyncing peerUdpAddrList for roomId=%v, playerId=%d", pR.Id, otherPlayerId))
pR.sendSafely(&pb.RoomDownsyncFrame{
PeerUdpAddrList: peerUdpAddrList,
}, nil, DOWNSYNC_MSG_ACT_PEER_UDP_ADDR, otherPlayerId, false, peerJoinIndex)
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"go.uber.org/zap" "go.uber.org/zap"
"net"
"net/http" "net/http"
"strconv" "strconv"
"sync/atomic" "sync/atomic"
@ -256,17 +257,19 @@ func Serve(c *gin.Context) {
SpaceOffsetX: pRoom.SpaceOffsetX, SpaceOffsetX: pRoom.SpaceOffsetX,
SpaceOffsetY: pRoom.SpaceOffsetY, SpaceOffsetY: pRoom.SpaceOffsetY,
RenderCacheSize: pRoom.RenderCacheSize, RenderCacheSize: pRoom.RenderCacheSize,
CollisionMinStep: pRoom.CollisionMinStep, CollisionMinStep: pRoom.CollisionMinStep,
BoundRoomCapacity: int32(pRoom.Capacity),
FrameDataLoggingEnabled: pRoom.FrameDataLoggingEnabled, FrameDataLoggingEnabled: pRoom.FrameDataLoggingEnabled,
} }
resp := &pb.WsResp{ resp := &pb.WsResp{
Ret: int32(Constants.RetCode.Ok), Ret: int32(Constants.RetCode.Ok),
EchoedMsgId: int32(0), EchoedMsgId: int32(0),
Act: models.DOWNSYNC_MSG_ACT_HB_REQ, Act: models.DOWNSYNC_MSG_ACT_HB_REQ,
BciFrame: bciFrame, BciFrame: bciFrame,
PeerJoinIndex: pThePlayer.JoinIndex,
} }
Logger.Debug("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp)) Logger.Debug("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp))
@ -432,12 +435,12 @@ func HandleSecondaryWsSessionForPlayer(c *gin.Context) {
playerId, err := models.GetPlayerIdByToken(token) playerId, err := models.GetPlayerIdByToken(token)
if err != nil || playerId == 0 { if err != nil || playerId == 0 {
// TODO: Abort with specific message. // TODO: Abort with specific message.
Logger.Warn("Secondary ws session playerLogin record not found for ws authentication:", zap.Any("intAuthToken", token)) Logger.Warn("Secondary ws session playerLogin record not found:", zap.Any("intAuthToken", token))
c.AbortWithStatus(http.StatusBadRequest) c.AbortWithStatus(http.StatusBadRequest)
return return
} }
Logger.Info("Secondary ws session playerLogin record has been found for ws authentication:", zap.Any("playerId", playerId), zap.Any("intAuthToken", token), zap.Any("boundRoomId", boundRoomId)) Logger.Info("Secondary ws session playerLogin record has been found:", zap.Any("playerId", playerId), zap.Any("intAuthToken", token), zap.Any("boundRoomId", boundRoomId))
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil { if err != nil {
@ -482,3 +485,32 @@ func HandleSecondaryWsSessionForPlayer(c *gin.Context) {
pRoom.SetSecondarySession(int32(playerId), conn, signalToCloseConnOfThisPlayer) pRoom.SetSecondarySession(int32(playerId), conn, signalToCloseConnOfThisPlayer)
} }
func HandleUdpHolePunchingForPlayer(message []byte, peerAddr *net.UDPAddr) {
pReq := new(pb.HolePunchUpsync)
if unmarshalErr := proto.Unmarshal(message, pReq); nil != unmarshalErr {
Logger.Error("Udp session failed to unmarshal", zap.Error(unmarshalErr))
return
}
token := pReq.IntAuthToken
boundRoomId := pReq.BoundRoomId
pRoom, existent := (*models.RoomMapManagerIns)[int32(boundRoomId)]
// Deliberately querying playerId after querying room, because the former is against persistent storage and could be slow!
if !existent {
Logger.Warn("Udp session failed to get:\n", zap.Any("intAuthToken", token), zap.Any("forBoundRoomId", boundRoomId))
return
}
// TODO: Wrap the following 2 stmts by sql transaction!
playerId, err := models.GetPlayerIdByToken(token)
if err != nil || playerId == 0 {
// TODO: Abort with specific message.
Logger.Warn("Udp session playerLogin record not found for:", zap.Any("intAuthToken", token))
return
}
Logger.Info("Udp session playerLogin record has been found:", zap.Any("playerId", playerId), zap.Any("intAuthToken", token), zap.Any("boundRoomId", boundRoomId), zap.Any("peerAddr", peerAddr))
pRoom.UpdatePeerUdpAddrList(int32(playerId), peerAddr, pReq)
}

View File

@ -77,22 +77,12 @@ message WsReq {
HeartbeatUpsync hb = 8; HeartbeatUpsync hb = 8;
} }
message WsResp {
int32 ret = 1;
int32 echoedMsgId = 2;
int32 act = 3;
RoomDownsyncFrame rdf = 4;
repeated InputFrameDownsync inputFrameDownsyncBatch = 5;
BattleColliderInfo bciFrame = 6;
int32 peerJoinIndex = 7; // Only used when "InputsBufferSnapshot.peerJoinIndex" is used.
}
message InputsBufferSnapshot { message InputsBufferSnapshot {
int32 refRenderFrameId = 1; int32 refRenderFrameId = 1;
uint64 unconfirmedMask = 2; uint64 unconfirmedMask = 2;
repeated InputFrameDownsync toSendInputFrameDownsyncs = 3; repeated InputFrameDownsync toSendInputFrameDownsyncs = 3;
bool shouldForceResync = 4; bool shouldForceResync = 4;
int32 peerJoinIndex = 5; // Only used when "WsResp.peerJoinIndex" is used. int32 peerJoinIndex = 5;
} }
message MeleeBullet { message MeleeBullet {
@ -191,10 +181,23 @@ message BattleColliderInfo {
double spaceOffsetX = 11; double spaceOffsetX = 11;
double spaceOffsetY = 12; double spaceOffsetY = 12;
int32 collisionMinStep = 13; int32 collisionMinStep = 13;
int32 boundRoomCapacity = 14;
bool frameDataLoggingEnabled = 1024; bool frameDataLoggingEnabled = 1024;
} }
message HolePunchUpsync {
string intAuthToken = 1;
int32 boundRoomId = 2;
int32 authKey = 3;
}
message PeerUdpAddr {
string ip = 1;
int32 port = 2;
int32 authKey = 3;
}
message RoomDownsyncFrame { message RoomDownsyncFrame {
int32 id = 1; int32 id = 1;
repeated PlayerDownsync playersArr = 2; repeated PlayerDownsync playersArr = 2;
@ -207,11 +210,15 @@ message RoomDownsyncFrame {
repeated int32 speciesIdList = 1026; repeated int32 speciesIdList = 1026;
int32 bulletLocalIdCounter = 1027; int32 bulletLocalIdCounter = 1027;
repeated PeerUdpAddr peerUdpAddrList = 1028;
} }
message HolePunchUpsync { message WsResp {
int32 joinIndex = 1; int32 ret = 1;
string intAuthToken = 2; int32 echoedMsgId = 2;
int32 boundRoomId = 3; int32 act = 3;
int32 authKey = 4; RoomDownsyncFrame rdf = 4;
repeated InputFrameDownsync inputFrameDownsyncBatch = 5;
BattleColliderInfo bciFrame = 6;
int32 peerJoinIndex = 7;
} }

View File

@ -54,7 +54,7 @@ cc.Class({
exitBtnOnClick(evt) { exitBtnOnClick(evt) {
window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
window.closeWSConnection(); window.closeWSConnection(constants.RET_CODE.UNKNOWN_ERROR, "");
cc.director.loadScene('login'); cc.director.loadScene('login');
}, },

View File

@ -336,7 +336,6 @@ cc.Class({
self.recentRenderCache = new RingBuffer(self.renderCacheSize); self.recentRenderCache = new RingBuffer(self.renderCacheSize);
self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others".
self.recentInputCache = gopkgs.NewRingBufferJs((self.renderCacheSize >> 1) + 1); self.recentInputCache = gopkgs.NewRingBufferJs((self.renderCacheSize >> 1) + 1);
self.gopkgsCollisionSys = gopkgs.NewCollisionSpaceJs((self.spaceOffsetX << 1), (self.spaceOffsetY << 1), self.collisionMinStep, self.collisionMinStep); self.gopkgsCollisionSys = gopkgs.NewCollisionSpaceJs((self.spaceOffsetX << 1), (self.spaceOffsetY << 1), self.collisionMinStep, self.collisionMinStep);
@ -500,7 +499,7 @@ cc.Class({
const fullPathOfTmxFile = cc.js.formatStr("map/%s/map", parsedBattleColliderInfo.stageName); const fullPathOfTmxFile = cc.js.formatStr("map/%s/map", parsedBattleColliderInfo.stageName);
cc.loader.loadRes(fullPathOfTmxFile, cc.TiledMapAsset, (err, tmxAsset) => { cc.loader.loadRes(fullPathOfTmxFile, cc.TiledMapAsset, (err, tmxAsset) => {
if (null != err) { if (null != err) {
console.error(err); console.error(`Error occurred when loading tiled stage ${parsedBattleColliderInfo.stageName}`, err);
return; return;
} }
@ -549,10 +548,6 @@ cc.Class({
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter); const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
self.gopkgsCollisionSysMap[collisionBarrierIndex] = newBarrierCollider; self.gopkgsCollisionSysMap[collisionBarrierIndex] = newBarrierCollider;
} }
self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
Object.assign(self.selfPlayerInfo, {
Id: self.selfPlayerInfo.playerId
});
self.initDebugDrawers(); self.initDebugDrawers();
const reqData = window.pb.protos.WsReq.encode({ const reqData = window.pb.protos.WsReq.encode({
msgId: Date.now(), msgId: Date.now(),
@ -920,7 +915,7 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
return; return;
} }
self._stringifyRdfIdToActuallyUsedInput(); self._stringifyRdfIdToActuallyUsedInput();
window.closeWSConnection(constants.RET_CODE.BATTLE_STOPPED); window.closeWSConnection(constants.RET_CODE.BATTLE_STOPPED, "");
self.battleState = ALL_BATTLE_STATES.IN_SETTLEMENT; self.battleState = ALL_BATTLE_STATES.IN_SETTLEMENT;
self.countdownNanos = null; self.countdownNanos = null;
if (self.musicEffectManagerScriptIns) { if (self.musicEffectManagerScriptIns) {
@ -1273,24 +1268,6 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
} }
const j = gopkgs.ConvertToDelayedInputFrameId(i); const j = gopkgs.ConvertToDelayedInputFrameId(i);
const delayedInputFrame = self.recentInputCache.GetByFrameId(j); const delayedInputFrame = self.recentInputCache.GetByFrameId(j);
/*
const prevJ = gopkgs.ConvertToDelayedInputFrameId(i - 1);
const prevDelayedInputFrame = self.recentInputCache.GetByFrameId(prevJ);
const prevBtnALevel = (null == prevDelayedInputFrame ? 0 : ((prevDelayedInputFrame.InputList[self.selfPlayerInfo.JoinIndex - 1] >> 4) & 1));
const btnALevel = ((delayedInputFrame.InputList[self.selfPlayerInfo.JoinIndex - 1] >> 4) & 1);
if (
ATK_CHARACTER_STATE.Atk1[0] == currRdf.PlayersArr[self.selfPlayerInfo.JoinIndex - 1].CharacterState
||
ATK_CHARACTER_STATE.Atk2[0] == currRdf.PlayersArr[self.selfPlayerInfo.JoinIndex - 1].CharacterState
) {
console.log(`rdf.Id=${i}, (btnALevel,j)=(${btnALevel},${j}), (prevBtnALevel,prevJ) is (${prevBtnALevel},${prevJ}), in cancellable atk!`);
}
if (btnALevel > 0) {
if (btnALevel > prevBtnALevel) {
console.log(`rdf.Id=${i}, rising edge of btnA triggered`);
}
}
*/
if (self.frameDataLoggingEnabled) { if (self.frameDataLoggingEnabled) {
const actuallyUsedInputClone = delayedInputFrame.InputList.slice(); const actuallyUsedInputClone = delayedInputFrame.InputList.slice();
@ -1336,7 +1313,7 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
const selfPlayerId = self.selfPlayerInfo.Id; const selfPlayerId = self.selfPlayerInfo.Id;
if (selfPlayerId == playerId) { if (selfPlayerId == playerId) {
self.selfPlayerInfo.JoinIndex = immediatePlayerInfo.JoinIndex; self.selfPlayerInfo.JoinIndex = immediatePlayerInfo.JoinIndex; // Update here in case of any change during WAITING phase
nodeAndScriptIns[1].showArrowTipNode(); nodeAndScriptIns[1].showArrowTipNode();
} }
} }

View File

@ -156,13 +156,12 @@ cc.Class({
cc.log(`#2 Js called back by CPP: onUdpMessage: ${JSON.stringify(echoed)}`); cc.log(`#2 Js called back by CPP: onUdpMessage: ${JSON.stringify(echoed)}`);
}; };
const res1 = DelayNoMore.UdpSession.openUdpSession(8888 + self.selfPlayerInfo.JoinIndex); const res1 = DelayNoMore.UdpSession.openUdpSession(8888 + self.selfPlayerInfo.JoinIndex);
const holePunchDate = window.pb.protos.HolePunchUpsync.encode({ const holePunchData = window.pb.protos.HolePunchUpsync.encode({
joinIndex: self.selfPlayerInfo.JoinIndex,
boundRoomId: 22, boundRoomId: 22,
intAuthToken: "foobar", intAuthToken: "foobar",
authKey: Math.floor(Math.random() * 65535), authKey: Math.floor(Math.random() * 65535),
}).finish() }).finish()
const res2 = DelayNoMore.UdpSession.punchToServer("127.0.0.1", 3000, holePunchDate); const res2 = DelayNoMore.UdpSession.punchToServer("127.0.0.1", 3000, holePunchData);
const res3 = DelayNoMore.UdpSession.upsertPeerUdpAddr(self.selfPlayerInfo.JoinIndex, "192.168.31.194", 6789, 123456, 2, self.selfPlayerInfo.JoinIndex); const res3 = DelayNoMore.UdpSession.upsertPeerUdpAddr(self.selfPlayerInfo.JoinIndex, "192.168.31.194", 6789, 123456, 2, self.selfPlayerInfo.JoinIndex);
//const res4 = DelayNoMore.UdpSession.closeUdpSession(); //const res4 = DelayNoMore.UdpSession.closeUdpSession();
} }

View File

@ -12,6 +12,7 @@ window.DOWNSYNC_MSG_ACT_INPUT_BATCH = 2;
window.DOWNSYNC_MSG_ACT_BATTLE_STOPPED = 3; window.DOWNSYNC_MSG_ACT_BATTLE_STOPPED = 3;
window.DOWNSYNC_MSG_ACT_FORCED_RESYNC = 4; window.DOWNSYNC_MSG_ACT_FORCED_RESYNC = 4;
window.DOWNSYNC_MSG_ACT_PEER_INPUT_BATCH = 5; window.DOWNSYNC_MSG_ACT_PEER_INPUT_BATCH = 5;
window.DOWNSYNC_MSG_ACT_PEER_UDP_ADDR = 6;
window.sendSafely = function(msgStr) { window.sendSafely = function(msgStr) {
/** /**
@ -46,9 +47,19 @@ window.getBoundRoomIdFromPersistentStorage = function() {
return cc.sys.localStorage.getItem("boundRoomId"); return cc.sys.localStorage.getItem("boundRoomId");
}; };
window.getBoundRoomCapacityFromPersistentStorage = function() {
const boundRoomIdExpiresAt = parseInt(cc.sys.localStorage.getItem("boundRoomIdExpiresAt"));
if (!boundRoomIdExpiresAt || Date.now() >= boundRoomIdExpiresAt) {
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
return null;
}
return cc.sys.localStorage.getItem("boundRoomCapacity");
};
window.clearBoundRoomIdInBothVolatileAndPersistentStorage = function() { window.clearBoundRoomIdInBothVolatileAndPersistentStorage = function() {
window.boundRoomId = null; window.boundRoomId = null;
cc.sys.localStorage.removeItem("boundRoomId"); cc.sys.localStorage.removeItem("boundRoomId");
cc.sys.localStorage.removeItem("boundRoomCapacity");
cc.sys.localStorage.removeItem("boundRoomIdExpiresAt"); cc.sys.localStorage.removeItem("boundRoomIdExpiresAt");
}; };
@ -57,20 +68,44 @@ window.clearSelfPlayer = function() {
}; };
window.boundRoomId = getBoundRoomIdFromPersistentStorage(); window.boundRoomId = getBoundRoomIdFromPersistentStorage();
window.boundRoomCapacity = getBoundRoomCapacityFromPersistentStorage();
window.handleHbRequirements = function(resp) { window.handleHbRequirements = function(resp) {
console.log(`Handle hb requirements #1`);
if (constants.RET_CODE.OK != resp.ret) return; if (constants.RET_CODE.OK != resp.ret) return;
if (null == window.boundRoomId) { // The assignment of "window.mapIns" is inside "Map.onLoad", which precedes "initPersistentSessionClient".
window.mapIns.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')); // This field is kept for distinguishing "self" and "others".
window.mapIns.selfPlayerInfo.Id = window.mapIns.selfPlayerInfo.playerId;
window.mapIns.selfPlayerInfo.JoinIndex = resp.peerJoinIndex;
console.log(`Handle hb requirements #2`);
if (null == window.boundRoomId || null == window.boundRoomCapacity) {
window.boundRoomId = resp.bciFrame.boundRoomId; window.boundRoomId = resp.bciFrame.boundRoomId;
window.boundRoomCapacity = resp.bciFrame.boundRoomCapacity;
cc.sys.localStorage.setItem('boundRoomId', window.boundRoomId); cc.sys.localStorage.setItem('boundRoomId', window.boundRoomId);
cc.sys.localStorage.setItem('boundRoomCapacity', window.boundRoomCapacity);
cc.sys.localStorage.setItem('boundRoomIdExpiresAt', Date.now() + 10 * 60 * 1000); // Temporarily hardcoded, for `boundRoomId` only. cc.sys.localStorage.setItem('boundRoomIdExpiresAt', Date.now() + 10 * 60 * 1000); // Temporarily hardcoded, for `boundRoomId` only.
} }
console.log(`Handle hb requirements #3`);
if (window.handleBattleColliderInfo) { if (window.handleBattleColliderInfo) {
if (!cc.sys.isNative) {
window.initSecondarySession(null, window.boundRoomId);
}
window.handleBattleColliderInfo(resp.bciFrame); window.handleBattleColliderInfo(resp.bciFrame);
} }
console.log(`Handle hb requirements #4`);
if (!cc.sys.isNative) {
console.log(`Handle hb requirements #5, web`);
window.initSecondarySession(null, window.boundRoomId);
} else {
console.log(`Handle hb requirements #5, native`);
const res1 = DelayNoMore.UdpSession.openUdpSession(8888 + window.mapIns.selfPlayerInfo.JoinIndex);
const intAuthToken = window.mapIns.selfPlayerInfo.intAuthToken;
const authKey = Math.floor(Math.random() * 65535);
window.mapIns.selfPlayerInfo.authKey = authKey;
const holePunchData = window.pb.protos.HolePunchUpsync.encode({
boundRoomId: window.boundRoomId,
intAuthToken: intAuthToken,
authKey: authKey,
}).finish();
const res2 = DelayNoMore.UdpSession.punchToServer(backendAddress.HOST, 3000, holePunchData);
}
}; };
function _uint8ToBase64(uint8Arr) { function _uint8ToBase64(uint8Arr) {
@ -128,6 +163,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
urlToConnect = urlToConnect + "&expectedRoomId=" + expectedRoomId; urlToConnect = urlToConnect + "&expectedRoomId=" + expectedRoomId;
} else { } else {
window.boundRoomId = getBoundRoomIdFromPersistentStorage(); window.boundRoomId = getBoundRoomIdFromPersistentStorage();
window.boundRoomCapacity = getBoundRoomCapacityFromPersistentStorage();
if (null != window.boundRoomId) { if (null != window.boundRoomId) {
console.log("initPersistentSessionClient with boundRoomId == " + boundRoomId); console.log("initPersistentSessionClient with boundRoomId == " + boundRoomId);
urlToConnect = urlToConnect + "&boundRoomId=" + window.boundRoomId; urlToConnect = urlToConnect + "&boundRoomId=" + window.boundRoomId;
@ -178,6 +214,16 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
} }
mapIns.onRoomDownsyncFrame(resp.rdf, resp.inputFrameDownsyncBatch); mapIns.onRoomDownsyncFrame(resp.rdf, resp.inputFrameDownsyncBatch);
break; break;
case window.DOWNSYNC_MSG_ACT_PEER_UDP_ADDR:
console.warn(`Got DOWNSYNC_MSG_ACT_PEER_UDP_ADDR resp=${JSON.stringify(resp, null, 2)}`);
if (cc.sys.isNative) {
const peerJoinIndex = resp.peerJoinIndex;
const peerAddrList = resp.rdf.peerUdpAddrList;
const peerAddr = peerAddrList[peerJoinIndex - 1];
console.log(`Got DOWNSYNC_MSG_ACT_PEER_UDP_ADDR peerAddr=${peerAddr}; boundRoomCapacity=${window.boundRoomCapacity}, mapIns.selfPlayerInfo=${window.mapIns.selfPlayerInfo}`);
DelayNoMore.UdpSession.upsertPeerUdpAddr(peerJoinIndex, peerAddr.ip, peerAddr.port, peerAddr.authKey, window.boundRoomCapacity, window.mapIns.selfPlayerInfo.JoinIndex);
}
break;
default: default:
break; break;
} }
@ -224,6 +270,9 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
default: default:
break; break;
} }
if (cc.sys.isNative) {
DelayNoMore.UdpSession.closeUdpSession();
}
}; };
}; };
@ -234,7 +283,7 @@ window.clearLocalStorageAndBackToLoginScene = function(shouldRetainBoundRoomIdIn
window.mapIns.musicEffectManagerScriptIns.stopAllMusic(); window.mapIns.musicEffectManagerScriptIns.stopAllMusic();
} }
window.closeWSConnection(); window.closeWSConnection(constants.RET_CODE.UNKNOWN_ERROR, "");
window.clearSelfPlayer(); window.clearSelfPlayer();
if (true != shouldRetainBoundRoomIdInBothVolatileAndPersistentStorage) { if (true != shouldRetainBoundRoomIdInBothVolatileAndPersistentStorage) {
window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.clearBoundRoomIdInBothVolatileAndPersistentStorage();

View File

@ -25,6 +25,7 @@
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0' and exists('$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A')">v140_xp</PlatformToolset> <PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0' and exists('$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A')">v140_xp</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset> <PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0' and exists('$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A')">v140_xp</PlatformToolset> <PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0' and exists('$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A')">v140_xp</PlatformToolset>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
@ -206,4 +207,4 @@ copy "$(ProjectDir)..\..\..\project.json" "$(OutDir)\" /Y</Command>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -17,8 +17,7 @@
}, },
"encryptJs": false, "encryptJs": false,
"excludeScenes": [ "excludeScenes": [
"92160186-3e0d-4e0a-ae20-97286170ba58", "8491a86c-bec9-4813-968a-128ca01639e0"
"2ff474d9-0c9e-4fe3-87ec-fbff7cae85b4"
], ],
"fb-instant-games": {}, "fb-instant-games": {},
"includeSDKBox": false, "includeSDKBox": false,
@ -38,7 +37,7 @@
"REMOTE_SERVER_ROOT": "", "REMOTE_SERVER_ROOT": "",
"orientation": "portrait" "orientation": "portrait"
}, },
"startScene": "8491a86c-bec9-4813-968a-128ca01639e0", "startScene": "2ff474d9-0c9e-4fe3-87ec-fbff7cae85b4",
"title": "DelayNoMore", "title": "DelayNoMore",
"webOrientation": "landscape", "webOrientation": "landscape",
"wechatgame": { "wechatgame": {
@ -58,5 +57,5 @@
"packageName": "org.genxium.delaynomore" "packageName": "org.genxium.delaynomore"
}, },
"win32": {}, "win32": {},
"includeAnySDK": null "includeAnySDK": false
} }

View File

@ -73,7 +73,7 @@
"shelter_z_reducer", "shelter_z_reducer",
"shelter" "shelter"
], ],
"last-module-event-record-time": 1673930863015, "last-module-event-record-time": 1674632533161,
"simulator-orientation": false, "simulator-orientation": false,
"simulator-resolution": { "simulator-resolution": {
"height": 640, "height": 640,