feat: complete native client rendering parity updates

This commit is contained in:
shen
2026-09-27 19:44:41 -07:00
parent 9a9dc6d224
commit 25dd65f2ce
99 changed files with 21121 additions and 921 deletions
+190 -5
View File
@@ -8,6 +8,7 @@
#include "EterBase/Timer.h"
#include "EterLib/Util.h"
#include "EterLib/GrpBase.h"
#include "EterLib/Camera.h"
#include "../EterLib/HostCursor.h"
#include "UserInterface/PythonNetworkStream.h"
#include "UserInterface/AccountConnector.h"
@@ -86,7 +87,11 @@ std::unique_ptr<CEffectManager> g_effect_manager;
class GameApplicationAdapter final : public IAbstractApplication
{
public:
void GetMousePosition(POINT* point) override { if (point) *point = {}; }
void GetMousePosition(POINT* point) override
{
if (point)
CPythonApplication::Instance().GetMousePosition(point);
}
float GetGlobalTime() override { return CTimer::Instance().GetCurrentSecond(); }
float GetGlobalElapsedTime() override { return CTimer::Instance().GetElapsedSecond(); }
void SkipRenderBuffering(DWORD) override {}
@@ -144,6 +149,7 @@ struct GameSingletons
CPythonSystem pySystem; // last CPythonApplication member (m_pySystem)
};
std::unique_ptr<GameSingletons> g_game_singletons;
bool g_host_hardware_cursor_enabled = false;
bool fail(std::string* error, const std::string& text)
{
@@ -229,6 +235,26 @@ void init_2v0_gameplay_stubs();
namespace PythonBoot
{
void SetHostHardwareCursorEnabled(bool enabled)
{
g_host_hardware_cursor_enabled = enabled;
}
bool HostHardwareCursorEnabled()
{
return g_host_hardware_cursor_enabled;
}
int CursorShape()
{
return g_game_singletons ? CPythonApplication::Instance().GetCursorNum() : 0;
}
bool CursorVisible()
{
return !g_game_singletons || CPythonApplication::Instance().GetCursorVisible();
}
std::string CurrentMapName()
{
if (!g_game_singletons)
@@ -255,9 +281,43 @@ void SetUISize(int width, int height)
}
}
void update_3d_pick_ray()
{
if (!g_game_singletons || !g_window_manager)
return;
if (!CPythonBackground::Instance().IsMapReady())
return;
long lx = 0, ly = 0;
g_window_manager->GetMousePosition(lx, ly);
const long sw = g_window_manager->GetScreenWidth();
const long sh = g_window_manager->GetScreenHeight();
if (sw <= 0 || sh <= 0)
return;
CScreen s;
s.SetPerspective(30.0f, g_window_manager->GetAspect(), 100.0f, CPythonBackground::Instance().GetFarClip());
s.UpdateViewMatrix();
s.BuildViewFrustum();
s.SetCursorPosition(lx, ly, sw, sh);
POINT ptMouse{static_cast<LONG>(lx), static_cast<LONG>(ly)};
CPythonItem::Instance().Update(ptMouse);
CPythonCharacterManager::Instance().Pick();
}
void UIMouseMove(int x, int y)
{
MtHostSetCursor(x, y);
if (g_game_singletons)
{
CPythonApplication::Instance().OnMouseMove(x, y);
if (g_window_manager)
{
long lx = x, ly = y;
g_window_manager->GetMousePosition(lx, ly);
MtHostSetCursor(static_cast<int>(lx), static_cast<int>(ly));
}
update_3d_pick_ray();
return;
}
if (g_window_manager) g_window_manager->RunMouseMove(x, y);
}
@@ -265,16 +325,55 @@ void UIMouseButton(int button, bool pressed, int x, int y)
{
MtHostSetCursor(x, y);
if (!g_window_manager) return;
g_window_manager->RunMouseMove(x, y);
if (g_game_singletons)
CPythonApplication::Instance().OnMouseMove(x, y);
else
g_window_manager->RunMouseMove(x, y);
update_3d_pick_ray();
switch (button)
{
case 1: if (pressed) g_window_manager->RunMouseLeftButtonDown(x, y); else g_window_manager->RunMouseLeftButtonUp(x, y); break;
case 2: if (pressed) g_window_manager->RunMouseRightButtonDown(x, y); else g_window_manager->RunMouseRightButtonUp(x, y); break;
case 3: if (pressed) g_window_manager->RunMouseMiddleButtonDown(x, y); else g_window_manager->RunMouseMiddleButtonUp(x, y); break;
case 1:
if (pressed)
g_window_manager->RunMouseLeftButtonDown(x, y);
else
g_window_manager->RunMouseLeftButtonUp(x, y);
break;
case 2:
if (pressed)
{
g_window_manager->RunMouseRightButtonDown(x, y);
}
else
{
g_window_manager->RunMouseRightButtonUp(x, y);
if (g_game_singletons)
{
CCamera* pkCmrCur = CCameraManager::Instance().GetCurrentCamera();
if (pkCmrCur && pkCmrCur->IsDraging())
CPythonApplication::Instance().OnMouseMiddleButtonUp(x, y);
}
}
break;
case 3:
if (g_game_singletons)
{
if (pressed) CPythonApplication::Instance().OnMouseMiddleButtonDown(x, y);
else CPythonApplication::Instance().OnMouseMiddleButtonUp(x, y);
}
if (pressed) g_window_manager->RunMouseMiddleButtonDown(x, y); else g_window_manager->RunMouseMiddleButtonUp(x, y);
break;
default: break;
}
}
void UIMouseWheel(int nLen)
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
pkCmrCur->Wheel(nLen);
}
// 40250 CPythonApplication::OnKeyDown/OnKeyUp (PythonApplicationEvent.cpp:130-146): ESC first goes
// to RunPressEscapeKey (the OnPressEscapeKey chain every dialog closes on), then to RunKeyDown.
void UIKey(int key, bool pressed)
@@ -282,12 +381,80 @@ void UIKey(int key, bool pressed)
if (!g_window_manager) return;
if (pressed)
{
CPythonApplication::Instance().KeyDown(key);
if (DIK_ESCAPE == key)
g_window_manager->RunPressEscapeKey();
g_window_manager->RunKeyDown(key);
}
else
{
CPythonApplication::Instance().KeyUp(key);
g_window_manager->RunKeyUp(key);
}
}
void SetMoveDirection(float angleDeg, bool moving)
{
if (!g_game_singletons) return;
if (moving)
CPythonPlayer::Instance().NEW_MoveToDirection(angleDeg);
else
CPythonPlayer::Instance().NEW_Stop();
}
void SetAttackKey(bool pressed)
{
if (!g_game_singletons) return;
CPythonPlayer::Instance().SetAttackKeyState(pressed);
}
void CameraBeginDrag(int x, int y)
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
pkCmrCur->BeginDrag(x, y);
}
void CameraDrag(int x, int y)
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
{
POINT pt;
pkCmrCur->Drag(x, y, &pt);
}
}
void CameraEndDrag()
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
pkCmrCur->EndDrag();
}
bool IsPointInsideActiveUI(int x, int y)
{
if (!g_window_manager) return false;
return g_window_manager->IsPointInsideActiveUI(x, y);
}
PlayerStatusInfo GetPlayerStatusInfo()
{
PlayerStatusInfo info{};
if (!g_game_singletons) return info;
info.level = CPythonPlayer::Instance().GetStatus(POINT_LEVEL);
info.hp = CPythonPlayer::Instance().GetStatus(POINT_HP);
info.max_hp = CPythonPlayer::Instance().GetStatus(POINT_MAX_HP);
info.sp = CPythonPlayer::Instance().GetStatus(POINT_SP);
info.max_sp = CPythonPlayer::Instance().GetStatus(POINT_MAX_SP);
info.exp = CPythonPlayer::Instance().GetStatus(POINT_EXP);
info.max_exp = CPythonPlayer::Instance().GetStatus(POINT_NEXT_EXP);
const char* szName = CPythonPlayer::Instance().GetName();
if (szName) info.name = szName;
return info;
}
// 40250 CPythonApplication::WindowProcedure (PythonApplicationProcedure.cpp:111-117): WM_CHAR goes to
@@ -346,6 +513,14 @@ void UIRender()
if (g_window_manager) g_window_manager->Render();
}
bool IsSoftwareCursorVisible()
{
if (!g_game_singletons)
return false;
return CPythonApplication::Instance().GetCursorMode() == CPythonApplication::CURSOR_MODE_SOFTWARE &&
CPythonApplication::Instance().GetCursorVisible();
}
bool Start(const char* stdlib_path, std::string* error)
{
if (g_launcher)
@@ -647,6 +822,16 @@ void Stop()
g_fiber.finished = false;
// 40250 Main(): pyLauncher.Clear() runs before app->Destroy()/delete app, so the window manager
// (a CPythonApplication member) is still alive while Py_Finalize runs the windows' __del__.
if (Py_IsInitialized())
{
PyGILState_STATE gil = PyGILState_Ensure();
PyRun_SimpleString(
"import sys\n"
"_mm = sys.modules.get('mouseModule')\n"
"if _mm and hasattr(_mm, 'mouseController'):\n"
" getattr(_mm.mouseController, 'cursorDict', {}).clear()\n");
PyGILState_Release(gil);
}
g_launcher->Clear();
g_game_singletons.reset();
g_game_application.reset();