mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-24 10:48:48 +00:00
Further updates on win32 build templates.
This commit is contained in:
parent
58b06f6a10
commit
0168e2182e
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
.vs
|
||||
**/.vs
|
||||
battle_srv/test_cases/test_cases
|
||||
battle_srv/test_cases/tests
|
||||
*.pid
|
||||
|
25
README.md
25
README.md
@ -108,4 +108,27 @@ Moreover, in practice I found that to spot sync anomalies, the following tools a
|
||||
- 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`.
|
||||
![networkstats](./charts/networkstats.png)
|
||||
![networkstats](./charts/networkstats.png)
|
||||
|
||||
### 2.3 WIN32 platform tool versioning
|
||||
![visual_studio](./charts/VisualStudioSetup.png)
|
||||
When building for native platforms, it's much more convenient to trigger the CocosCreator project forming frmo CLI, e.g.
|
||||
```
|
||||
shell> cd <proj-root>
|
||||
shell> /path/to/CocosCreator.exe --path ./frontend --build "platform=win32;debug=true"
|
||||
```
|
||||
|
||||
### 2.4 CococCreator native build reloading
|
||||
CocosCreator doesn't have perfect file cache management during native project building, use "Developer Tools > Reload" to reset the IDE status upon mysterious errors.
|
||||
![ccc_reload](./charts/NativeBuildReload.png)
|
||||
|
||||
### 2.5 Checking UDP port binding result
|
||||
__*nix__
|
||||
```
|
||||
netstat -anp | grep <your_port>
|
||||
```
|
||||
|
||||
__Windows__
|
||||
```
|
||||
netstat -ano | grep <your_port>
|
||||
```
|
||||
|
File diff suppressed because one or more lines are too long
BIN
charts/NativeBuildReload.png
Normal file
BIN
charts/NativeBuildReload.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
Binary file not shown.
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 417 KiB |
@ -362,7 +362,7 @@
|
||||
"array": [
|
||||
0,
|
||||
0,
|
||||
216.50635094610968,
|
||||
216.67832656600567,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
@ -547,7 +547,7 @@
|
||||
"array": [
|
||||
0,
|
||||
0,
|
||||
210.72485222798673,
|
||||
210.25627653647612,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
@ -14,7 +14,7 @@ NetworkDoctor.prototype.reset = function(capacity) {
|
||||
|
||||
this.inputRateThreshold = gopkgs.ConvertToNoDelayInputFrameId(60);
|
||||
this.peerUpsyncThreshold = 8;
|
||||
this.rollbackFramesThreshold = 4; // Slightly smaller than the minimum "TurnAroundFramesToRecover".
|
||||
this.rollbackFramesThreshold = 8; // Roughly the minimum "TurnAroundFramesToRecover".
|
||||
};
|
||||
|
||||
NetworkDoctor.prototype.logSending = function(stFrameId, edFrameId) {
|
||||
|
@ -144,6 +144,11 @@ cc.Class({
|
||||
Id: 10,
|
||||
JoinIndex: 1,
|
||||
};
|
||||
if (cc.sys.isNative) {
|
||||
DelayNoMore.UdpSession.upsertPeerUdpAddr(self.selfPlayerInfo.JoinIndex, "192.168.31.194", 6789, 123456);
|
||||
const res1 = DelayNoMore.UdpSession.openUdpSession(8888 + self.selfPlayerInfo.JoinIndex);
|
||||
//const res2 = DelayNoMore.UdpSession.closeUdpSession();
|
||||
}
|
||||
self.onRoomDownsyncFrame(startRdf);
|
||||
|
||||
self.battleState = ALL_BATTLE_STATES.IN_BATTLE;
|
||||
@ -154,12 +159,6 @@ cc.Class({
|
||||
update(dt) {
|
||||
const self = this;
|
||||
if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) {
|
||||
const elapsedMillisSinceLastFrameIdTriggered = performance.now() - self.lastRenderFrameIdTriggeredAt;
|
||||
if (elapsedMillisSinceLastFrameIdTriggered < self.tooFastDtIntervalMillis) {
|
||||
// [WARNING] We should avoid a frontend ticking too fast to prevent cheating, as well as ticking too slow to cause a "resync avalanche" that impacts user experience!
|
||||
// console.debug("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": elapsedMillisSinceLastFrameIdTriggered=", elapsedMillisSinceLastFrameIdTriggered);
|
||||
//return;
|
||||
}
|
||||
try {
|
||||
let st = performance.now();
|
||||
let prevSelfInput = null,
|
||||
|
File diff suppressed because one or more lines are too long
@ -39,6 +39,7 @@
|
||||
"frameworks/runtime-src/proj.android-studio/app/res/values/strings.xml",
|
||||
"frameworks/runtime-src/Classes/udp_session.hpp",
|
||||
"frameworks/runtime-src/Classes/udp_session.cpp",
|
||||
"frameworks/runtime-src/Classes/udp_session_bridge.hpp",
|
||||
"frameworks/runtime-src/Classes/udp_session_bridge.cpp",
|
||||
"frameworks/runtime-src/Classes/AppDelegate.cpp"
|
||||
]
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
|
||||
#include "cocos/scripting/js-bindings/event/EventDispatcher.h"
|
||||
#include "cocos/scripting/js-bindings/manual/jsb_classtype.hpp"
|
||||
|
||||
#include "udp_session_bridge.hpp"
|
||||
USING_NS_CC;
|
||||
|
||||
AppDelegate::AppDelegate(int width, int height) : Application("Cocos Game", width, height)
|
||||
@ -61,6 +61,7 @@ bool AppDelegate::applicationDidFinishLaunching()
|
||||
});
|
||||
|
||||
jsb_register_all_modules();
|
||||
se->addRegisterCallback(registerUdpSession);
|
||||
|
||||
se->start();
|
||||
|
||||
|
@ -1,14 +1,95 @@
|
||||
#include "udp_session.hpp"
|
||||
#include "base/ccMacros.h"
|
||||
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
|
||||
#include <stdio.h>
|
||||
#include "uv/uv.h"
|
||||
#include <thread>
|
||||
|
||||
static uv_udp_t* udpSocket = NULL;
|
||||
uv_loop_t* loop = NULL; // Only this loop is used for this simple PoC
|
||||
|
||||
void _onRead(uv_udp_t* req, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) {
|
||||
if (nread < 0) {
|
||||
CCLOGERROR("Read error %s", uv_err_name(nread));
|
||||
uv_close((uv_handle_t*)req, NULL);
|
||||
free(buf->base);
|
||||
return;
|
||||
}
|
||||
|
||||
char sender[17] = { 0 };
|
||||
uv_ip4_name((const struct sockaddr_in*)addr, sender, 16);
|
||||
CCLOG("Recv from %s", sender);
|
||||
|
||||
free(buf->base);
|
||||
uv_udp_recv_stop(req);
|
||||
}
|
||||
|
||||
static void _allocBuffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
(void)handle;
|
||||
buf->base = (char *)malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
void startRecvLoop(void* arg) {
|
||||
uv_loop_t* loop = (uv_loop_t*)arg;
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
|
||||
bool DelayNoMore::UdpSession::openUdpSession(int port) {
|
||||
CCLOG("About to open UDP session at port=%d...", port);
|
||||
loop = uv_loop_new(); // Only the default loop is used for this simple PoC
|
||||
udpSocket = (uv_udp_t*)malloc(sizeof(uv_udp_t));
|
||||
SOCKADDR_IN recv_addr;
|
||||
uv_ip4_addr("0.0.0.0", port, &recv_addr);
|
||||
|
||||
uv_udp_init(loop, udpSocket);
|
||||
|
||||
uv_udp_bind(udpSocket, (struct sockaddr const*)&recv_addr, UV_UDP_REUSEADDR);
|
||||
uv_udp_recv_start(udpSocket, _allocBuffer, _onRead);
|
||||
|
||||
uv_thread_t recvTid;
|
||||
uv_thread_create(&recvTid, startRecvLoop, loop);
|
||||
|
||||
//std::thread([=]() {
|
||||
// udpSocket = (uv_udp_t*)malloc(sizeof(uv_udp_t));
|
||||
// SOCKADDR_IN recv_addr;
|
||||
// uv_ip4_addr("0.0.0.0", port, &recv_addr);
|
||||
//
|
||||
// uv_udp_init(loop, udpSocket);
|
||||
//
|
||||
// uv_udp_bind(udpSocket, (struct sockaddr const*)&recv_addr, UV_UDP_REUSEADDR);
|
||||
// uv_udp_recv_start(udpSocket, _allocBuffer, _onRead);
|
||||
//
|
||||
// startRecvLoop(loop);
|
||||
//}).detach();
|
||||
|
||||
CCLOG("Finished opening UDP session at port=%d", port);
|
||||
|
||||
bool DelayNoMore::UdpSession::upsertPeerUdpAddr(int joinIndex, CHARC* const ip, int port, CHARC* const authKey) {
|
||||
printf("Called by js for joinIndex=%d, ip=%s, port=%d, authKey=%s.", joinIndex, ip, port, authKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DelayNoMore::UdpSession::onMessage(CBYTE* const bytes) {
|
||||
static void _onWalkCleanup(uv_handle_t* handle, void* data) {
|
||||
(void)data;
|
||||
uv_close(handle, NULL);
|
||||
}
|
||||
|
||||
bool DelayNoMore::UdpSession::closeUdpSession() {
|
||||
CCLOG("About to close udp session and dealloc all resources...");
|
||||
uv_stop(loop);
|
||||
uv_walk(loop, _onWalkCleanup, NULL);
|
||||
uv_loop_close(loop);
|
||||
free(udpSocket);
|
||||
free(loop);
|
||||
CCLOG("Closed udp session and dealloc all resources...");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DelayNoMore::UdpSession::upsertPeerUdpAddr(int joinIndex, CHARC* const ip, int port, uint32_t authKey) {
|
||||
CCLOG("Called by js for joinIndex=%d, ip=%s, port=%d, authKey=%lu.", joinIndex, ip, port, authKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DelayNoMore::UdpSession::onMessage(BYTEC* const bytes) {
|
||||
se::ScriptEngine* se = se::ScriptEngine::getInstance();
|
||||
|
||||
se::Value func;
|
||||
|
@ -3,16 +3,18 @@
|
||||
|
||||
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
|
||||
|
||||
typedef unsigned char const CBYTE;
|
||||
typedef unsigned char const BYTEC;
|
||||
typedef char const CHARC;
|
||||
|
||||
namespace DelayNoMore {
|
||||
class UdpSession {
|
||||
public:
|
||||
static bool upsertPeerUdpAddr(int joinIndex, CHARC* const ip, int port, CHARC* const authKey);
|
||||
static bool openUdpSession(int port);
|
||||
static bool closeUdpSession();
|
||||
static bool upsertPeerUdpAddr(int joinIndex, CHARC* const ip, int port, uint32_t authKey);
|
||||
//static bool clearPeerUDPAddrList();
|
||||
//static void punchToServer(CBYTE* const bytes);
|
||||
static void onMessage(CBYTE* const bytes);
|
||||
static void onMessage(BYTEC* const bytes);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -2,6 +2,35 @@
|
||||
#include "base/ccMacros.h"
|
||||
#include "scripting/js-bindings/manual/jsb_conversions.hpp"
|
||||
|
||||
bool openUdpSession(se::State& s) {
|
||||
const auto& args = s.args();
|
||||
size_t argc = args.size();
|
||||
CC_UNUSED bool ok = true;
|
||||
if (1 == argc) {
|
||||
SE_PRECONDITION2(ok, false, "openUdpSession: Error processing arguments");
|
||||
int port = args[0].toInt32();
|
||||
return DelayNoMore::UdpSession::openUdpSession(port);
|
||||
}
|
||||
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
SE_BIND_FUNC(openUdpSession)
|
||||
|
||||
bool closeUdpSession(se::State& s) {
|
||||
const auto& args = s.args();
|
||||
size_t argc = args.size();
|
||||
CC_UNUSED bool ok = true;
|
||||
if (0 == argc) {
|
||||
SE_PRECONDITION2(ok, false, "closeUdpSession: Error processing arguments");
|
||||
return DelayNoMore::UdpSession::closeUdpSession();
|
||||
}
|
||||
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
SE_BIND_FUNC(closeUdpSession)
|
||||
|
||||
bool upsertPeerUdpAddr(se::State& s) {
|
||||
const auto& args = s.args();
|
||||
size_t argc = args.size();
|
||||
@ -11,13 +40,12 @@ bool upsertPeerUdpAddr(se::State& s) {
|
||||
int joinIndex = args[0].toInt32();
|
||||
CHARC* ip = args[1].toString().c_str();
|
||||
int port = args[2].toInt32();
|
||||
CHARC* authKey = args[3].toString().c_str();
|
||||
DelayNoMore::UdpSession::upsertPeerUdpAddr(joinIndex, ip, port, authKey);
|
||||
return true;
|
||||
uint32_t authKey = args[3].toUint32();
|
||||
return DelayNoMore::UdpSession::upsertPeerUdpAddr(joinIndex, ip, port, authKey);
|
||||
}
|
||||
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 4);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
SE_BIND_FUNC(upsertPeerUdpAddr)
|
||||
|
||||
@ -36,7 +64,7 @@ SE_BIND_FINALIZE_FUNC(udpSessionFinalize)
|
||||
|
||||
se::Object* __jsb_udp_session_proto = nullptr;
|
||||
se::Class* __jsb_udp_session_class = nullptr;
|
||||
bool register_udp_session(se::Object* obj)
|
||||
bool registerUdpSession(se::Object* obj)
|
||||
{
|
||||
// Get the ns
|
||||
se::Value nsVal;
|
||||
@ -49,7 +77,9 @@ bool register_udp_session(se::Object* obj)
|
||||
|
||||
se::Object* ns = nsVal.toObject();
|
||||
auto cls = se::Class::create("UdpSession", ns, nullptr, nullptr);
|
||||
|
||||
|
||||
cls->defineStaticFunction("openUdpSession", _SE(openUdpSession));
|
||||
cls->defineStaticFunction("closeUdpSession", _SE(closeUdpSession));
|
||||
cls->defineStaticFunction("upsertPeerUdpAddr", _SE(upsertPeerUdpAddr));
|
||||
cls->defineFinalizeFunction(_SE(udpSessionFinalize));
|
||||
cls->install();
|
||||
|
@ -8,8 +8,10 @@
|
||||
extern se::Object* __jsb_udp_session_proto;
|
||||
extern se::Class* __jsb_udp_session_class;
|
||||
|
||||
bool register_udp_session(se::Object* obj);
|
||||
bool registerUdpSession(se::Object* obj);
|
||||
|
||||
SE_DECLARE_FUNC(openUdpSession);
|
||||
SE_DECLARE_FUNC(closeUdpSession);
|
||||
SE_DECLARE_FUNC(upsertPeerUdpAddr);
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="resource">
|
||||
<UniqueIdentifier>{ca9c9e15-d942-43a1-aa7a-5f0b74ca1afd}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;png;manifest</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="win32">
|
||||
<UniqueIdentifier>{ccb2323b-1cfa-41ea-bcf4-ba5f07309396}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Classes">
|
||||
<UniqueIdentifier>{e93a77e1-af1e-4400-87d3-504b62ebdbb0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Classes\AppDelegate.cpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Classes\jsb_module_register.cpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Classes\udp_session.cpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Classes\udp_session_bridge.cpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Classes\AppDelegate.h">
|
||||
<Filter>Classes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>win32</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="..\Classes\udp_session.hpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Classes\udp_session_bridge.hpp">
|
||||
<Filter>Classes</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="game.rc">
|
||||
<Filter>resource</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="res\game.ico">
|
||||
<Filter>resource</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -2,7 +2,7 @@
|
||||
"android-instant": {
|
||||
"REMOTE_SERVER_ROOT": "",
|
||||
"host": "",
|
||||
"packageName": "org.cocos2d.helloworld",
|
||||
"packageName": "org.genxium.delaynomore",
|
||||
"pathPattern": "",
|
||||
"recordPath": "",
|
||||
"scheme": "https",
|
||||
@ -17,15 +17,14 @@
|
||||
},
|
||||
"encryptJs": false,
|
||||
"excludeScenes": [
|
||||
"2ff474d9-0c9e-4fe3-87ec-fbff7cae85b4",
|
||||
"92160186-3e0d-4e0a-ae20-97286170ba58"
|
||||
"8491a86c-bec9-4813-968a-128ca01639e0"
|
||||
],
|
||||
"fb-instant-games": {},
|
||||
"includeSDKBox": false,
|
||||
"inlineSpriteFrames": true,
|
||||
"inlineSpriteFrames_native": true,
|
||||
"inlineSpriteFrames_native": false,
|
||||
"md5Cache": false,
|
||||
"mergeStartScene": false,
|
||||
"mergeStartScene": true,
|
||||
"optimizeHotUpdate": false,
|
||||
"orientation": {
|
||||
"landscapeLeft": true,
|
||||
@ -33,12 +32,12 @@
|
||||
"portrait": false,
|
||||
"upsideDown": false
|
||||
},
|
||||
"packageName": "org.cocos2d.helloworld",
|
||||
"packageName": "org.genxium.delaynomore",
|
||||
"qqplay": {
|
||||
"REMOTE_SERVER_ROOT": "",
|
||||
"orientation": "portrait"
|
||||
},
|
||||
"startScene": "8491a86c-bec9-4813-968a-128ca01639e0",
|
||||
"startScene": "2ff474d9-0c9e-4fe3-87ec-fbff7cae85b4",
|
||||
"title": "DelayNoMore",
|
||||
"webOrientation": "landscape",
|
||||
"wechatgame": {
|
||||
@ -49,14 +48,14 @@
|
||||
"xxteaKey": "4d54a3d5-e6f3-49",
|
||||
"zipCompressJs": true,
|
||||
"android": {
|
||||
"packageName": "org.cocos2d.tsrht"
|
||||
"packageName": "org.genxium.delaynomore"
|
||||
},
|
||||
"ios": {
|
||||
"packageName": "org.cocos2d.helloworld"
|
||||
"packageName": "org.genxium.delaynomore"
|
||||
},
|
||||
"mac": {
|
||||
"packageName": "org.cocos2d.helloworld"
|
||||
"packageName": "org.genxium.delaynomore"
|
||||
},
|
||||
"win32": {},
|
||||
"includeAnySDK": false
|
||||
"includeAnySDK": null
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "TEMPLATES.helloworld.name",
|
||||
"desc": "TEMPLATES.helloworld.desc",
|
||||
"name": "DelayNoMore",
|
||||
"desc": "DelayNoMore",
|
||||
"banner": "template-banner.png"
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ const (
|
||||
GRAVITY_X = int32(0)
|
||||
GRAVITY_Y = -int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO) // makes all "playerCollider.Y" a multiple of 0.5 in all cases
|
||||
|
||||
INPUT_DELAY_FRAMES = int32(4) // in the count of render frames
|
||||
INPUT_DELAY_FRAMES = int32(6) // in the count of render frames
|
||||
INPUT_SCALE_FRAMES = uint32(2) // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
|
||||
NST_DELAY_FRAMES = int32(16) // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
|
||||
|
||||
@ -669,9 +669,11 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
|
||||
*/
|
||||
//fmt.Printf("joinIndex=%d is not wall jumping and not aligned w/ inertia\n{renderFrame.id: %d, effDx: %d, thatPlayerInNextFrame.VelX: %d}\n", currPlayerDownsync.JoinIndex, currRenderFrame.Id, effDx, thatPlayerInNextFrame.VelX)
|
||||
thatPlayerInNextFrame.CapturedByInertia = true
|
||||
thatPlayerInNextFrame.FramesToRecover = chConfig.InertiaFramesToRecover
|
||||
if exactTurningAround {
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_TURNAROUND
|
||||
thatPlayerInNextFrame.FramesToRecover = chConfig.InertiaFramesToRecover
|
||||
} else {
|
||||
thatPlayerInNextFrame.FramesToRecover = (chConfig.InertiaFramesToRecover >> 1)
|
||||
}
|
||||
} else {
|
||||
thatPlayerInNextFrame.CapturedByInertia = false
|
||||
|
Loading…
Reference in New Issue
Block a user