Adapt Android mobile controls and native client UI

This commit is contained in:
shen
2026-09-28 19:39:13 -07:00
parent a1ae69aa7a
commit 3c52ced02e
180 changed files with 688 additions and 23782 deletions
+3 -6
View File
@@ -1,6 +1,5 @@
# port_logic — mirror of the 40250 client logic (extension/src/port/<Lib>/<File>.{h,cpp}).
# No godot-cpp dependency: libmtgodot links it, and it also builds on its own (top-level
# -DMTGODOT_BUILD_EXTENSION=OFF -DMTGODOT_BUILD_PORT=ON) for targets without an extension build.
# This library and port_platform are built directly for the SDL/Vulkan client and port tests.
# See docs/PORT-PLAN.md batch 2A.
file(GLOB_RECURSE MT_PORT_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
@@ -31,8 +30,7 @@ target_include_directories(port_logic PUBLIC ${MT_PORT_SHIMS})
# Libraries 40250 links that are vendored as-is: <lzo/lzo1x.h> (EterBase/lzo.h), <cryptopp/*>
# (EterBase/cipher.h), <Python-2.7/*> (ScriptLib).
target_link_libraries(port_logic PUBLIC mt3p::minilzo mt3p::cryptopp)
# common/Win32Crt.cpp converts ANSI code pages with mt_codepage. extension/CMakeLists.txt adds it for
# mtnet; the port-only build (script/port_gate.sh, native_render) never reads that file.
# common/Win32Crt.cpp converts ANSI code pages with mt_codepage.
if(NOT TARGET mt_codepage)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../codepage ${CMAKE_CURRENT_BINARY_DIR}/../codepage)
endif()
@@ -241,8 +239,7 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
# Locale packs in the original format (script/build_zh_locale.py packs locale/zh with it).
add_executable(eterpack_pack ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/eterpack_pack.cpp)
target_link_libraries(eterpack_pack PRIVATE port_platform)
# 2V2-e: the same fake server as a process, for the Godot-driven render run
# (script/python_game_render_test.sh).
# The same fake server as a process, for native rendering tests.
add_executable(port_fake_login_server ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_fake_login_server_main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_login_flow_server.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../net/classic/classic_cipher.cpp)
+38
View File
@@ -33,6 +33,32 @@
#include <iostream>
#include <fstream>
#if !defined(_WIN32)
#include <dirent.h> // PORT: resolve separately cased .eix/.epk names on case-sensitive mobile filesystems
#include <strings.h> // PORT: strcasecmp for the on-disk pack name lookup
// PORT: 40250 ships pairs such as ETC.eix and Etc.epk. Windows opens both through the
// same stem; Android must locate each file using its actual on-disk spelling.
static std::string ResolvePackFileName(const std::string& requested)
{
const std::string::size_type slash = requested.find_last_of('/');
const std::string folder = slash == std::string::npos ? "." : requested.substr(0, slash);
const std::string name = requested.substr(slash == std::string::npos ? 0 : slash + 1);
DIR* dir = opendir(folder.c_str());
if (!dir) return requested;
std::string result = requested;
while (dirent* entry = readdir(dir))
{
if (strcasecmp(entry->d_name, name.c_str()) == 0)
{
result = folder + "/" + entry->d_name;
break;
}
}
closedir(dir);
return result;
}
#endif
void CMakePackLog::SetFileName(const char* c_szFileName)
{
@@ -257,6 +283,18 @@ bool CEterPack::Create(CEterFileDict& rkFileDict, const char * dbname, const cha
m_stDataFileName = dbname;
m_stDataFileName += ".epk";
// PORT: the two extensions may have different capitalization in the original Client/pack.
#if !defined(_WIN32)
if (bReadOnly)
{
const std::string indexName = ResolvePackFileName(m_indexFileName);
if (indexName.size() < sizeof(m_indexFileName))
{
strcpy(m_indexFileName, indexName.c_str());
}
m_stDataFileName = ResolvePackFileName(m_stDataFileName);
}
#endif
m_bReadOnly = bReadOnly;
@@ -1,4 +1,6 @@
#include "StdAfx.h"
#include <algorithm>
#include <cctype>
#include "../EterBase/CRC32.h"
#include "PythonWindow.h"
#include "PythonSlotWindow.h"
@@ -1392,7 +1394,7 @@ namespace UI
return FALSE;
}
CExpandedImageBox::CExpandedImageBox(PyObject * ppyObject) : CImageBox(ppyObject)
CExpandedImageBox::CExpandedImageBox(PyObject * ppyObject) : CImageBox(ppyObject), m_bAspectFill(false)
{
}
CExpandedImageBox::~CExpandedImageBox()
@@ -1400,6 +1402,73 @@ namespace UI
OnDestroyInstance();
}
void CExpandedImageBox::SetAspectFill(bool bEnable)
{
m_bAspectFill = bEnable;
}
bool CExpandedImageBox::IsAutoAspectFillBackground() const
{
const char* c_szFileName = "";
if (m_pImageInstance && m_pImageInstance->GetGraphicImagePointer())
c_szFileName = m_pImageInstance->GetGraphicImagePointer()->GetFileName();
if (!c_szFileName)
c_szFileName = "";
std::string lowerFile = c_szFileName;
std::transform(lowerFile.begin(), lowerFile.end(), lowerFile.begin(), ::tolower);
const std::string& name = m_strName;
std::string parentName = m_pParent ? m_pParent->GetName() : "";
// 1. Server list and login backgrounds (LoginWindow bg1 and bg2)
if (name == "bg1" || name == "bg2")
{
if (parentName == "LoginWindow")
return true;
if (lowerFile.find("serverlist") != std::string::npos || lowerFile.find("login") != std::string::npos)
return true;
}
// 2. Character selection & creation backgrounds (BackGround with select.sub / select.jpg)
if (name == "BackGround")
{
if (parentName == "SelectCharacterWindow" || parentName == "CreateCharacterWindow")
return true;
if (lowerFile.find("select.sub") != std::string::npos ||
lowerFile.find("select.jpg") != std::string::npos ||
lowerFile.find("select.png") != std::string::npos ||
lowerFile.find("ui/select") != std::string::npos)
return true;
}
// 3. Loading screen backgrounds (BackGround with Line_Pattern.tga or uiloading/*.sub)
if (name == "BackGround")
{
if (parentName == "LoadingWindow")
return true;
if (lowerFile.find("uiloading") != std::string::npos ||
lowerFile.find("line_pattern") != std::string::npos)
return true;
}
// Fallback check by file name directly if element name wasn't set or is generic
if (lowerFile.find("serverlist.sub") != std::string::npos ||
lowerFile.find("serverlist.jpg") != std::string::npos ||
lowerFile.find("uiloading/") != std::string::npos ||
lowerFile.find("uiloading\\") != std::string::npos)
{
return true;
}
return false;
}
bool CExpandedImageBox::IsAspectFill() const
{
return m_bAspectFill || IsAutoAspectFillBackground();
}
void CExpandedImageBox::OnCreateInstance()
{
OnDestroyInstance();
@@ -1420,9 +1489,56 @@ namespace UI
if (!m_pImageInstance)
return;
if (IsAspectFill())
{
long screenWidth = UI::CWindowManager::Instance().GetScreenWidth();
long screenHeight = UI::CWindowManager::Instance().GetScreenHeight();
int imgWidth = GetWidth();
int imgHeight = GetHeight();
if (screenWidth > 0 && screenHeight > 0 && imgWidth > 0 && imgHeight > 0)
{
float sx = float(screenWidth) / float(imgWidth);
float sy = float(screenHeight) / float(imgHeight);
float s = std::max(sx, sy);
((CGraphicExpandedImageInstance*)m_pImageInstance)->SetScale(s, s);
CWindow::SetSize(screenWidth, screenHeight);
float posX = (float(screenWidth) - float(imgWidth) * s) * 0.5f;
float posY = (float(screenHeight) - float(imgHeight) * s) * 0.5f;
m_pImageInstance->SetPosition(posX, posY);
return;
}
}
((CGraphicExpandedImageInstance*)m_pImageInstance)->SetScale(fx, fy);
CWindow::SetSize(long(float(GetWidth())*fx), long(float(GetHeight())*fy));
}
void CExpandedImageBox::OnChangePosition()
{
if (!m_pImageInstance)
return;
if (IsAspectFill())
{
long screenWidth = UI::CWindowManager::Instance().GetScreenWidth();
long screenHeight = UI::CWindowManager::Instance().GetScreenHeight();
int imgWidth = GetWidth();
int imgHeight = GetHeight();
if (screenWidth > 0 && screenHeight > 0 && imgWidth > 0 && imgHeight > 0)
{
float sx = float(screenWidth) / float(imgWidth);
float sy = float(screenHeight) / float(imgHeight);
float s = std::max(sx, sy);
float posX = (float(screenWidth) - float(imgWidth) * s) * 0.5f;
float posY = (float(screenHeight) - float(imgHeight) * s) * 0.5f;
m_pImageInstance->SetPosition(posX, posY);
return;
}
}
CImageBox::OnChangePosition();
}
void CExpandedImageBox::SetOrigin(float fx, float fy)
{
if (!m_pImageInstance)
@@ -1458,6 +1574,24 @@ namespace UI
if (!m_pImageInstance)
return;
if (IsAspectFill())
{
long screenWidth = UI::CWindowManager::Instance().GetScreenWidth();
long screenHeight = UI::CWindowManager::Instance().GetScreenHeight();
int imgWidth = GetWidth();
int imgHeight = GetHeight();
if (screenWidth > 0 && screenHeight > 0 && imgWidth > 0 && imgHeight > 0)
{
float sx = float(screenWidth) / float(imgWidth);
float sy = float(screenHeight) / float(imgHeight);
float s = std::max(sx, sy);
((CGraphicExpandedImageInstance*)m_pImageInstance)->SetScale(s, s);
float posX = (float(screenWidth) - float(imgWidth) * s) * 0.5f;
float posY = (float(screenHeight) - float(imgHeight) * s) * 0.5f;
m_pImageInstance->SetPosition(posX, posY);
}
}
if (IsShow())
m_pImageInstance->Render();
}
@@ -386,6 +386,8 @@ namespace UI
void SetRotation(float fRotation);
void SetRenderingRect(float fLeft, float fTop, float fRight, float fBottom);
void SetRenderingMode(int iMode);
void SetAspectFill(bool bEnable);
bool IsAspectFill() const;
protected:
void OnCreateInstance();
@@ -393,8 +395,14 @@ namespace UI
virtual void OnUpdate();
virtual void OnRender();
virtual void OnChangePosition();
BOOL OnIsType(DWORD dwType);
bool IsAutoAspectFillBackground() const;
protected:
bool m_bAspectFill;
};
class CAniImageBox : public CWindow
{
@@ -2012,6 +2012,27 @@ PyObject * wndImageSetScale(PyObject * poSelf, PyObject * poArgs)
return Py_BuildNone();
}
PyObject * wndImageSetAspectFill(PyObject * poSelf, PyObject * poArgs)
{
UI::CWindow * pWindow;
if (!PyTuple_GetWindow(poArgs, 0, &pWindow))
return Py_BuildException();
bool bEnable = true;
if (PyTuple_Size(poArgs) > 1)
{
int iEnable;
if (PyTuple_GetInteger(poArgs, 1, &iEnable))
bEnable = (iEnable != 0);
}
if (pWindow->IsType(UI::CExpandedImageBox::Type()))
{
((UI::CExpandedImageBox*)pWindow)->SetAspectFill(bEnable);
}
return Py_BuildNone();
}
PyObject * wndImageSetOrigin(PyObject * poSelf, PyObject * poArgs)
{
UI::CWindow * pWindow;
@@ -2454,6 +2475,7 @@ void initwndMgr()
{ "GetHeight", wndImageGetHeight, METH_VARARGS },
// ExpandedImageBox
{ "SetScale", wndImageSetScale, METH_VARARGS },
{ "SetAspectFill", wndImageSetAspectFill, METH_VARARGS },
{ "SetOrigin", wndImageSetOrigin, METH_VARARGS },
{ "SetRotation", wndImageSetRotation, METH_VARARGS },
{ "SetRenderingRect", wndImageSetRenderingRect, METH_VARARGS },