Compare commits

..

3 Commits

Author SHA1 Message Date
genxium
1c6ad5c8f8 Minor fix. 2023-01-21 23:03:55 +08:00
genxium
34e0893eb8 Added network doctor for frontend. 2023-01-21 22:53:41 +08:00
genxium
cc7524becd Updated README. 2023-01-21 11:59:01 +08:00
12 changed files with 1420 additions and 1080 deletions

View File

@@ -3,19 +3,14 @@
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md). As lots of feedbacks ask for a discussion on using UDP instead, I tried to summarize my personal opinion about it in [ConcerningEdgeCases](./ConcerningEdgeCases.md) -- not necessarily correct but that's indeed a question to face :) This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md). As lots of feedbacks ask for a discussion on using UDP instead, I tried to summarize my personal opinion about it in [ConcerningEdgeCases](./ConcerningEdgeCases.md) -- not necessarily correct but that's indeed a question to face :)
The following video is recorded over INTERNET using an input delay of 4 frames and it feels SMOOTH when playing! Please also checkout these demo videos The following video is recorded over INTERNET using an input delay of 4 frames and it feels SMOOTH when playing! Please also checkout these demo videos
- [source video of the first gif](https://pan.baidu.com/s/1ML6hNupaPHPJRd5rcTvQvw?pwd=8ruc) - [source video of the first gif (earlier version)](https://pan.baidu.com/s/1ML6hNupaPHPJRd5rcTvQvw?pwd=8ruc)
- [phone v.s. PC over internet battle#1](https://pan.baidu.com/s/1NuGxuMwrV_jalcToyUZPLg?pwd=kfkr) - [source video of the second gif (added turn-around optimization & dashing)](https://pan.baidu.com/s/1isMcLvxax4NNkDgitV_FDg?pwd=s1i6)
- [phone v.s. PC over internet battle#2](https://pan.baidu.com/s/1kMiFdwDHyJpZJ0GGU1Y3eA?pwd=46gd)
- [PC Wifi viewing Phone 4g](https://pan.baidu.com/s/1PJEtC9iB_fcabMWhbx2oAg?pwd=tp7k)
- [PC Wifi viewing Phone Wifi (over internet of course)](https://pan.baidu.com/s/108rvC1CcUdiQeMauXWsLJg?pwd=mn39)
to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance. to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
![gif_demo_1](./charts/internet_fireball_explosion_wallmove_spedup.gif) ![gif_demo_1](./charts/internet_fireball_explosion_wallmove_spedup.gif)
![gif_demo_2](./charts/jump_sync_spedup.gif) ![gif_demo_2](./charts/internet_dash_turnaround_cut_spedup.gif)
All gifs are sped up to ~1.5x for file size reduction, kindly note that animations are resumed from a partial progress!
# Notable Features # Notable Features
- Backend dynamics toggle via [Room.BackendDynamicsEnabled](https://github.com/genxium/DelayNoMore/blob/v0.9.14/battle_srv/models/room.go#L786) - Backend dynamics toggle via [Room.BackendDynamicsEnabled](https://github.com/genxium/DelayNoMore/blob/v0.9.14/battle_srv/models/room.go#L786)
@@ -111,3 +106,5 @@ Moreover, in practice I found that to spot sync anomalies, the following tools a
- Detection of [prediction mismatch on the frontend](https://github.com/genxium/DelayNoMore/blob/v0.9.19/frontend/assets/scripts/Map.js#L842). - Detection of [prediction mismatch on the frontend](https://github.com/genxium/DelayNoMore/blob/v0.9.19/frontend/assets/scripts/Map.js#L842).
- Detection of [type#1 forceConfirmation on the backend](https://github.com/genxium/DelayNoMore/blob/v0.9.19/battle_srv/models/room.go#L1246). - Detection of [type#1 forceConfirmation on the backend](https://github.com/genxium/DelayNoMore/blob/v0.9.19/battle_srv/models/room.go#L1246).
- Detection of [type#2 forceConfirmation on the backend](https://github.com/genxium/DelayNoMore/blob/v0.9.19/battle_srv/models/room.go#L1259). - Detection of [type#2 forceConfirmation on the backend](https://github.com/genxium/DelayNoMore/blob/v0.9.19/battle_srv/models/room.go#L1259).
There's also some useful information displayed on the frontend when `true == Map.showNetworkDoctorInfo`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

View File

@@ -1,66 +0,0 @@
function NetworkDoctor(serverFps, clientUpsyncFps) {
this.serverFps = serverFps;
this.clientUpsyncFps = clientUpsyncFps;
this.millisPerServerFrame = parseInt(1000 / this.serverFps);
this._tooLongSinceLastFrameDiffReceivedThreshold = (this.millisPerServerFrame << 6);
this.setupFps = function(fps) {
this.serverFps = this.clientUpsyncFps = fps;
this.millisPerServerFrame = parseInt(1000 / this.serverFps);
this._tooLongSinceLastFrameDiffReceivedThreshold = (this.millisPerServerFrame << 6);
}
this._lastFrameDiffRecvTime = null;
this._tooLongSinceLastFrameDiffReceived = function() {
if (undefined === this._lastFrameDiffRecvTime || null === this._lastFrameDiffRecvTime) return false;
return (this._tooLongSinceLastFrameDiffReceivedThreshold <= (Date.now() - this._lastFrameDiffRecvTime));
};
this._consecutiveALittleLongFrameDiffReceivedIntervalCount = 0;
this._consecutiveALittleLongFrameDiffReceivedIntervalCountThreshold = 120;
this.onNewFrameDiffReceived = function(frameDiff) {
var now = Date.now();
if (undefined !== this._lastFrameDiffRecvTime && null !== this._lastFrameDiffRecvTime) {
var intervalFromLastFrameDiff = (now - this._lastFrameDiffRecvTime);
if ((this.millisPerServerFrame << 5) < intervalFromLastFrameDiff) {
++this._consecutiveALittleLongFrameDiffReceivedIntervalCount;
console.log('Medium delay, intervalFromLastFrameDiff is', intervalFromLastFrameDiff);
} else {
this._consecutiveALittleLongFrameDiffReceivedIntervalCount = 0;
}
}
this._lastFrameDiffRecvTime = now;
};
this._networkComplaintPrefix = "\nNetwork is not good >_<\n";
this.generateNetworkComplaint = function(excludeTypeConstantALittleLongFrameDiffReceivedInterval, excludeTypeTooLongSinceLastFrameDiffReceived) {
if (this.hasBattleStopped) return null;
var shouldComplain = false;
var ret = this._networkComplaintPrefix;
if (true != excludeTypeConstantALittleLongFrameDiffReceivedInterval && this._consecutiveALittleLongFrameDiffReceivedIntervalCountThreshold <= this._consecutiveALittleLongFrameDiffReceivedIntervalCount) {
this._consecutiveALittleLongFrameDiffReceivedIntervalCount = 0;
ret += "\nConstantly having a little long recv interval.\n";
shouldComplain = true;
}
if (true != excludeTypeTooLongSinceLastFrameDiffReceived && this._tooLongSinceLastFrameDiffReceived()) {
ret += "\nToo long since last received frameDiff.\n";
shouldComplain = true;
}
return (shouldComplain ? ret : null);
};
this.hasBattleStopped = false;
this.onBattleStopped = function() {
this.hasBattleStopped = true;
};
this.isClientSessionConnected = function() {
if (!window.game) return false;
if (!window.game.clientSession) return false;
return window.game.clientSession.connected;
};
}
window.NetworkDoctor = NetworkDoctor;

View File

@@ -78,19 +78,19 @@
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 40 "__id__": 48
}, },
{ {
"__id__": 41 "__id__": 49
}, },
{ {
"__id__": 42 "__id__": 50
}, },
{ {
"__id__": 43 "__id__": 51
}, },
{ {
"__id__": 44 "__id__": 52
} }
], ],
"_prefab": null, "_prefab": null,
@@ -158,7 +158,7 @@
"__id__": 5 "__id__": 5
}, },
{ {
"__id__": 39 "__id__": 47
} }
], ],
"_prefab": null, "_prefab": null,
@@ -247,10 +247,10 @@
"__uuid__": "670b477e-61a1-4778-879b-35913f7c79d2" "__uuid__": "670b477e-61a1-4778-879b-35913f7c79d2"
}, },
"boundRoomIdLabel": { "boundRoomIdLabel": {
"__id__": 16 "__id__": 24
}, },
"countdownLabel": { "countdownLabel": {
"__id__": 23 "__id__": 31
}, },
"resultPanelPrefab": { "resultPanelPrefab": {
"__uuid__": "c4cfe3bd-c59e-4d5b-95cb-c933b120e184" "__uuid__": "c4cfe3bd-c59e-4d5b-95cb-c933b120e184"
@@ -269,9 +269,18 @@
}, },
"forceBigEndianFloatingNumDecoding": false, "forceBigEndianFloatingNumDecoding": false,
"renderFrameIdLagTolerance": 4, "renderFrameIdLagTolerance": 4,
"jigglingEps1D": 0.001, "sendingQLabel": {
"bulletTriggerEnabled": true, "__id__": 13
"closeOnForcedtoResyncNotSelf": true, },
"inputFrameDownsyncQLabel": {
"__id__": 15
},
"peerInputFrameUpsyncQLabel": {
"__id__": 17
},
"rollbackFramesLabel": {
"__id__": 19
},
"_id": "d12gkAmppNlIzqcRDELa91" "_id": "d12gkAmppNlIzqcRDELa91"
}, },
{ {
@@ -283,13 +292,13 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 33 "__id__": 41
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 38 "__id__": 46
} }
], ],
"_prefab": null, "_prefab": null,
@@ -352,19 +361,19 @@
"__id__": 6 "__id__": 6
}, },
{ {
"__id__": 22 "__id__": 30
}, },
{ {
"__id__": 24 "__id__": 32
}, },
{ {
"__id__": 28 "__id__": 36
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 32 "__id__": 40
} }
], ],
"_prefab": null, "_prefab": null,
@@ -433,7 +442,7 @@
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 21 "__id__": 29
} }
], ],
"_prefab": null, "_prefab": null,
@@ -527,7 +536,7 @@
"array": [ "array": [
0, 0,
0, 0,
209.57814771583418, 216.50635094610968,
0, 0,
0, 0,
0, 0,
@@ -596,15 +605,27 @@
"_children": [ "_children": [
{ {
"__id__": 12 "__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
},
{
"__id__": 18
},
{
"__id__": 20
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 19 "__id__": 27
}, },
{ {
"__id__": 20 "__id__": 28
} }
], ],
"_prefab": null, "_prefab": null,
@@ -655,6 +676,374 @@
"groupIndex": 0, "groupIndex": 0,
"_id": "35scg40jVAnKrTPiei5ckg" "_id": "35scg40jVAnKrTPiei5ckg"
}, },
{
"__type__": "cc.Node",
"_name": "sendingQ",
"_objFlags": 0,
"_parent": {
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 13
}
],
"_prefab": null,
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 12.24,
"height": 28.98
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
64,
85.51,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": "ack7XH+0lEj6chSUsKBLU5"
},
{
"__type__": "cc.Label",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 12
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_useOriginalSize": false,
"_string": "0",
"_N$string": "0",
"_fontSize": 22,
"_lineHeight": 23,
"_enableWrapText": true,
"_N$file": null,
"_isSystemFontUsed": true,
"_spacingX": 0,
"_batchAsBitmap": false,
"_N$horizontalAlign": 0,
"_N$verticalAlign": 1,
"_N$fontFamily": "Arial",
"_N$overflow": 0,
"_N$cacheMode": 0,
"_id": "deCJfLuoFO36c/O4lXWJ1N"
},
{
"__type__": "cc.Node",
"_name": "inputFrameDownsyncQ",
"_objFlags": 0,
"_parent": {
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 15
}
],
"_prefab": null,
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 12.24,
"height": 28.98
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
64,
56.53000000000001,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": "d9n+NRm9pA0YtKxvy4bW+U"
},
{
"__type__": "cc.Label",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 14
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_useOriginalSize": false,
"_string": "0",
"_N$string": "0",
"_fontSize": 22,
"_lineHeight": 23,
"_enableWrapText": true,
"_N$file": null,
"_isSystemFontUsed": true,
"_spacingX": 0,
"_batchAsBitmap": false,
"_N$horizontalAlign": 0,
"_N$verticalAlign": 1,
"_N$fontFamily": "Arial",
"_N$overflow": 0,
"_N$cacheMode": 0,
"_id": "90NvjGFrpBFqqzl1WZczta"
},
{
"__type__": "cc.Node",
"_name": "peerInputFrameUpsyncQ",
"_objFlags": 0,
"_parent": {
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 17
}
],
"_prefab": null,
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 12.24,
"height": 28.98
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
64,
27.550000000000004,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": "45FAmgRLZNNbLt/GcnTXVx"
},
{
"__type__": "cc.Label",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 16
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_useOriginalSize": false,
"_string": "0",
"_N$string": "0",
"_fontSize": 22,
"_lineHeight": 23,
"_enableWrapText": true,
"_N$file": null,
"_isSystemFontUsed": true,
"_spacingX": 0,
"_batchAsBitmap": false,
"_N$horizontalAlign": 0,
"_N$verticalAlign": 1,
"_N$fontFamily": "Arial",
"_N$overflow": 0,
"_N$cacheMode": 0,
"_id": "42PRTrjEpDw6Z8N5yWFTpd"
},
{
"__type__": "cc.Node",
"_name": "rollbackFrames",
"_objFlags": 0,
"_parent": {
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 19
}
],
"_prefab": null,
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 12.24,
"height": 28.98
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
64,
-1.4299999999999962,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": "f5SBs3b1pPNbHbnqVLYsHp"
},
{
"__type__": "cc.Label",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 18
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_useOriginalSize": false,
"_string": "0",
"_N$string": "0",
"_fontSize": 22,
"_lineHeight": 23,
"_enableWrapText": true,
"_N$file": null,
"_isSystemFontUsed": true,
"_spacingX": 0,
"_batchAsBitmap": false,
"_N$horizontalAlign": 0,
"_N$verticalAlign": 1,
"_N$fontFamily": "Arial",
"_N$overflow": 0,
"_N$cacheMode": 0,
"_id": "77aNARt1VATLsjIzwbqvkh"
},
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
"_name": "RoomIdIndicator", "_name": "RoomIdIndicator",
@@ -664,19 +1053,19 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 13 "__id__": 21
}, },
{ {
"__id__": 15 "__id__": 23
} }
], ],
"_active": false, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 17 "__id__": 25
}, },
{ {
"__id__": 18 "__id__": 26
} }
], ],
"_prefab": null, "_prefab": null,
@@ -732,13 +1121,13 @@
"_name": "label", "_name": "label",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 20
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 14 "__id__": 22
} }
], ],
"_prefab": null, "_prefab": null,
@@ -794,7 +1183,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 13 "__id__": 21
}, },
"_enabled": true, "_enabled": true,
"_materials": [], "_materials": [],
@@ -822,13 +1211,13 @@
"_name": "BoundRoomIdLabel", "_name": "BoundRoomIdLabel",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 20
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 16 "__id__": 24
} }
], ],
"_prefab": null, "_prefab": null,
@@ -884,7 +1273,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 15 "__id__": 23
}, },
"_enabled": true, "_enabled": true,
"_materials": [], "_materials": [],
@@ -912,7 +1301,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 12 "__id__": 20
}, },
"_enabled": true, "_enabled": true,
"_layoutSize": { "_layoutSize": {
@@ -945,7 +1334,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 12 "__id__": 20
}, },
"_enabled": true, "_enabled": true,
"_materials": [], "_materials": [],
@@ -1067,7 +1456,7 @@
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 23 "__id__": 31
} }
], ],
"_prefab": null, "_prefab": null,
@@ -1123,7 +1512,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 22 "__id__": 30
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@@ -1157,7 +1546,7 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 25 "__id__": 33
} }
], ],
"_active": true, "_active": true,
@@ -1215,16 +1604,16 @@
"_name": "Background", "_name": "Background",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 24 "__id__": 32
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 26 "__id__": 34
}, },
{ {
"__id__": 27 "__id__": 35
} }
], ],
"_prefab": null, "_prefab": null,
@@ -1280,7 +1669,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 25 "__id__": 33
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@@ -1314,7 +1703,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 25 "__id__": 33
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@@ -1345,7 +1734,7 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 29 "__id__": 37
} }
], ],
"_active": true, "_active": true,
@@ -1403,16 +1792,16 @@
"_name": "Background", "_name": "Background",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 28 "__id__": 36
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 30 "__id__": 38
}, },
{ {
"__id__": 31 "__id__": 39
} }
], ],
"_prefab": null, "_prefab": null,
@@ -1468,7 +1857,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 29 "__id__": 37
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@@ -1502,7 +1891,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 29 "__id__": 37
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@@ -1560,16 +1949,16 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 34 "__id__": 42
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 36 "__id__": 44
}, },
{ {
"__id__": 37 "__id__": 45
} }
], ],
"_prefab": null, "_prefab": null,
@@ -1625,13 +2014,13 @@
"_name": "Joystick", "_name": "Joystick",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 33 "__id__": 41
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 35 "__id__": 43
} }
], ],
"_prefab": null, "_prefab": null,
@@ -1687,7 +2076,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 34 "__id__": 42
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@@ -1721,7 +2110,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 33 "__id__": 41
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@@ -1755,7 +2144,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 33 "__id__": 41
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@@ -1908,10 +2297,10 @@
"__id__": 3 "__id__": 3
}, },
"stickhead": { "stickhead": {
"__id__": 34 "__id__": 42
}, },
"base": { "base": {
"__id__": 33 "__id__": 41
}, },
"joyStickEps": 0.1, "joyStickEps": 0.1,
"magicLeanLowerBound": 0.414, "magicLeanLowerBound": 0.414,
@@ -1932,10 +2321,10 @@
"linearMovingEps": 0.1, "linearMovingEps": 0.1,
"scaleByEps": 0.0375, "scaleByEps": 0.0375,
"btnA": { "btnA": {
"__id__": 24 "__id__": 32
}, },
"btnB": { "btnB": {
"__id__": 28 "__id__": 36
}, },
"_id": "e9oVYTr7ROlpp/IrNjBUmR" "_id": "e9oVYTr7ROlpp/IrNjBUmR"
} }

View File

@@ -362,7 +362,7 @@
"array": [ "array": [
0, 0,
0, 0,
210.43934936178934, 216.50635094610968,
0, 0,
0, 0,
0, 0,

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ const i18n = require('LanguageData');
i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field
const RingBuffer = require('./RingBuffer'); const RingBuffer = require('./RingBuffer');
const NetworkDoctor = require('./NetworkDoctor');
const PriorityQueue = require("./PriorityQueue"); const PriorityQueue = require("./PriorityQueue");
window.ALL_MAP_STATES = { window.ALL_MAP_STATES = {
@@ -96,15 +97,21 @@ cc.Class({
type: cc.Integer, type: cc.Integer,
default: 4 // implies (renderFrameIdLagTolerance >> inputScaleFrames) count of inputFrameIds default: 4 // implies (renderFrameIdLagTolerance >> inputScaleFrames) count of inputFrameIds
}, },
jigglingEps1D: { sendingQLabel: {
type: cc.Float, type: cc.Label,
default: 1e-3 default: null
}, },
bulletTriggerEnabled: { inputFrameDownsyncQLabel: {
default: false type: cc.Label,
default: null
}, },
closeOnForcedtoResyncNotSelf: { peerInputFrameUpsyncQLabel: {
default: true type: cc.Label,
default: null
},
rollbackFramesLabel: {
type: cc.Label,
default: null
}, },
}, },
@@ -184,6 +191,7 @@ cc.Class({
// Upon resync, "self.lastUpsyncInputFrameId" might not have been updated properly. // Upon resync, "self.lastUpsyncInputFrameId" might not have been updated properly.
batchInputFrameIdSt = self.recentInputCache.StFrameId; batchInputFrameIdSt = self.recentInputCache.StFrameId;
} }
self.networkDoctor.logSending(batchInputFrameIdSt, latestLocalInputFrameId);
for (let i = batchInputFrameIdSt; i <= latestLocalInputFrameId; ++i) { for (let i = batchInputFrameIdSt; i <= latestLocalInputFrameId; ++i) {
const inputFrameDownsync = self.recentInputCache.GetByFrameId(i); const inputFrameDownsync = self.recentInputCache.GetByFrameId(i);
if (null == inputFrameDownsync) { if (null == inputFrameDownsync) {
@@ -342,6 +350,8 @@ cc.Class({
self.othersForcedDownsyncRenderFrameDict = new Map(); self.othersForcedDownsyncRenderFrameDict = new Map();
self.rdfIdToActuallyUsedInput = new Map(); self.rdfIdToActuallyUsedInput = new Map();
self.networkDoctor = new NetworkDoctor(20);
self.countdownNanos = null; self.countdownNanos = null;
if (self.countdownLabel) { if (self.countdownLabel) {
self.countdownLabel.string = ""; self.countdownLabel.string = "";
@@ -409,6 +419,7 @@ cc.Class({
}, },
onLoad() { onLoad() {
cc.game.setFrameRate(60);
cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE); cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE);
cc.view.enableAutoFullScreen(true); cc.view.enableAutoFullScreen(true);
@@ -417,6 +428,7 @@ cc.Class({
window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding; window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding;
self.showCriticalCoordinateLabels = false; self.showCriticalCoordinateLabels = false;
self.showNetworkDoctorInfo = true;
console.warn("+++++++ Map onLoad()"); console.warn("+++++++ Map onLoad()");
@@ -796,6 +808,7 @@ cc.Class({
return; return;
} }
self.networkDoctor.logInputFrameDownsync(batch[0].inputFrameId, batch[batch.length - 1].inputFrameId);
let firstPredictedYetIncorrectInputFrameId = null; let firstPredictedYetIncorrectInputFrameId = null;
for (let k in batch) { for (let k in batch) {
const inputFrameDownsync = batch[k]; const inputFrameDownsync = batch[k];
@@ -839,6 +852,7 @@ cc.Class({
-------------------------------------------------------- --------------------------------------------------------
*/ */
// The actual rollback-and-chase would later be executed in update(dt). // The actual rollback-and-chase would later be executed in update(dt).
self.networkDoctor.immediateRollbackFrames = (self.renderFrameId - renderFrameId1);
console.log(`Mismatched input detected, resetting chaserRenderFrameId: ${self.chaserRenderFrameId}->${renderFrameId1} by firstPredictedYetIncorrectInputFrameId: ${firstPredictedYetIncorrectInputFrameId} console.log(`Mismatched input detected, resetting chaserRenderFrameId: ${self.chaserRenderFrameId}->${renderFrameId1} by firstPredictedYetIncorrectInputFrameId: ${firstPredictedYetIncorrectInputFrameId}
lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId} lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}
recentInputCache=${self._stringifyRecentInputCache(false)} recentInputCache=${self._stringifyRecentInputCache(false)}
@@ -860,6 +874,7 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
return; return;
} }
self.networkDoctor.logPeerInputFrameUpsync(batch[0].inputFrameId, batch[batch.length - 1].inputFrameId);
//console.log(`Received peer inputFrameUpsync batch w/ inputFrameId in [${batch[0].inputFrameId}, ${batch[batch.length - 1].inputFrameId}] for prediction assistance`); //console.log(`Received peer inputFrameUpsync batch w/ inputFrameId in [${batch[0].inputFrameId}, ${batch[batch.length - 1].inputFrameId}] for prediction assistance`);
for (let k in batch) { for (let k in batch) {
const inputFrameDownsync = batch[k]; const inputFrameDownsync = batch[k];
@@ -1005,6 +1020,9 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
} }
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf); self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
self.showDebugBoundaries(rdf); self.showDebugBoundaries(rdf);
if (self.showNetworkDoctorInfo) {
self.showNetworkDoctorLabels();
}
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! ++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
self.lastRenderFrameIdTriggeredAt = performance.now(); self.lastRenderFrameIdTriggeredAt = performance.now();
let t3 = performance.now(); let t3 = performance.now();
@@ -1499,4 +1517,12 @@ actuallyUsedinputList:{${self.inputFrameDownsyncStr(actuallyUsedInputClone)}}`);
} }
} }
}, },
showNetworkDoctorLabels() {
const self = this;
self.sendingQLabel.string = self.networkDoctor.statSending();
self.inputFrameDownsyncQLabel.string = self.networkDoctor.statInputFrameDownsync();
self.peerInputFrameUpsyncQLabel.string = self.networkDoctor.statPeerInputFrameUpsync();
self.rollbackFramesLabel.string = self.networkDoctor.statRollbackFrames();
},
}); });

View File

@@ -0,0 +1,74 @@
const RingBuffer = require('./RingBuffer');
var NetworkDoctor = function(capacity) {
this.reset(capacity);
};
NetworkDoctor.prototype.reset = function(capacity) {
this.sendingQ = new RingBuffer(capacity);
this.inputFrameDownsyncQ = new RingBuffer(capacity);
this.peerInputFrameUpsyncQ = new RingBuffer(capacity);
this.peerInputFrameUpsyncCnt = 0;
this.immediateRollbackFrames = 0;
};
NetworkDoctor.prototype.logSending = function(stFrameId, edFrameId) {
this.sendingQ.put({
i: stFrameId,
j: edFrameId,
t: Date.now()
});
};
NetworkDoctor.prototype.logInputFrameDownsync = function(stFrameId, edFrameId) {
this.inputFrameDownsyncQ.put({
i: stFrameId,
j: edFrameId,
t: Date.now()
});
};
NetworkDoctor.prototype.logPeerInputFrameUpsync = function(stFrameId, edFrameId) {
const firstPopped = this.peerInputFrameUpsyncQ.put({
i: stFrameId,
j: edFrameId,
t: Date.now()
});
if (null != firstPopped) {
this.peerInputFrameUpsyncCnt -= (firstPopped.j - firstPopped.i + 1);
}
this.peerInputFrameUpsyncCnt += (edFrameId - stFrameId + 1);
};
NetworkDoctor.prototype.statSending = function() {
if (1 >= this.sendingQ.cnt) return `0 fps sending`;
const st = this.sendingQ.getByFrameId(this.sendingQ.stFrameId);
const ed = this.sendingQ.getByFrameId(this.sendingQ.edFrameId - 1);
const elapsedMillis = ed.t - st.t;
const fps = Math.round((ed.j - st.i) * 1000 / elapsedMillis);
return `${fps} fps sending`;
};
NetworkDoctor.prototype.statInputFrameDownsync = function() {
if (1 >= this.inputFrameDownsyncQ.cnt) return `0 fps srv downsync`;
const st = this.inputFrameDownsyncQ.getByFrameId(this.inputFrameDownsyncQ.stFrameId);
const ed = this.inputFrameDownsyncQ.getByFrameId(this.inputFrameDownsyncQ.edFrameId - 1);
const elapsedMillis = ed.t - st.t;
const fps = Math.round((ed.j - st.i) * 1000 / elapsedMillis);
return `${fps} fps srv downsync`;
};
NetworkDoctor.prototype.statPeerInputFrameUpsync = function() {
if (1 >= this.peerInputFrameUpsyncQ.cnt) return `0 fps peer upsync`;
const st = this.peerInputFrameUpsyncQ.getByFrameId(this.peerInputFrameUpsyncQ.stFrameId);
const ed = this.peerInputFrameUpsyncQ.getByFrameId(this.peerInputFrameUpsyncQ.edFrameId - 1);
const elapsedMillis = ed.t - st.t;
const fps = Math.round(this.peerInputFrameUpsyncCnt * 1000 / elapsedMillis);
return `${fps} fps peer upsync`;
};
NetworkDoctor.prototype.statRollbackFrames = function() {
return `${this.immediateRollbackFrames} rollback frames`;
};
module.exports = NetworkDoctor;

View File

@@ -1,6 +1,6 @@
{ {
"ver": "1.0.5", "ver": "1.0.5",
"uuid": "477c07c3-0d50-4d55-96f0-6eaf9f25e2da", "uuid": "affd726a-02f0-4079-aace-39fe525d7478",
"isPlugin": false, "isPlugin": false,
"loadPluginInWeb": true, "loadPluginInWeb": true,
"loadPluginInNative": true, "loadPluginInNative": true,

View File

@@ -11,11 +11,13 @@ cc.Class({
}, },
onLoad() { onLoad() {
cc.game.setFrameRate(60);
cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE); cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE);
cc.view.enableAutoFullScreen(true); cc.view.enableAutoFullScreen(true);
const self = this; const self = this;
window.mapIns = self; window.mapIns = self;
self.showCriticalCoordinateLabels = false; self.showCriticalCoordinateLabels = false;
self.showNetworkDoctorInfo = true;
const mapNode = self.node; const mapNode = self.node;
const canvasNode = mapNode.parent; const canvasNode = mapNode.parent;

View File

@@ -13,9 +13,12 @@ var RingBuffer = function(capacity) {
}; };
RingBuffer.prototype.put = function(item) { RingBuffer.prototype.put = function(item) {
let firstPopped = null;
while (0 < this.cnt && this.cnt >= this.n) { while (0 < this.cnt && this.cnt >= this.n) {
// Make room for the new element // Make room for the new element
this.pop(); const popped = this.pop();
if (null == firstPopped)
firstPopped = popped;
} }
this.eles[this.ed] = item this.eles[this.ed] = item
this.edFrameId++; this.edFrameId++;
@@ -24,6 +27,7 @@ RingBuffer.prototype.put = function(item) {
if (this.ed >= this.n) { if (this.ed >= this.n) {
this.ed -= this.n; // Deliberately not using "%" operator for performance concern this.ed -= this.n; // Deliberately not using "%" operator for performance concern
} }
return firstPopped;
}; };
RingBuffer.prototype.pop = function() { RingBuffer.prototype.pop = function() {