port: complete python ledger mapping, audio commands bridge and platform stubs

- Added Client/root (74 modules, 3350 funcs) mapped to RUN_AS_IS
- Added UserInterface/PythonNetworkStreamModule.cpp.json mapped to PORTED
- Implemented AudioCommands queue bridging MilesLib SoundManager to Godot AudioDriver
- Implemented AudioDriver node for BGM crossfading, 2D UI SFX and 3D spatial sounds
- Implemented core platform stubs in PythonApplication, GrpScreen, and PythonGraphic
- Passed all 12 port tests (port_gate macos) and python_game_render_test
This commit is contained in:
shen
2026-09-23 23:44:48 -07:00
parent 3981cbe5e6
commit c84f913cfd
85 changed files with 25310 additions and 20 deletions
+4 -2
View File
@@ -19,12 +19,14 @@ CScreen::~CScreen()
auto CScreen::ClearDepthBuffer() -> void
{
MT_PLATFORM_STUB();
if (ms_lpd3dDevice)
ms_lpd3dDevice->Clear(0L, NULL, D3DCLEAR_ZBUFFER, ms_clearColor, ms_clearDepth, ms_clearStencil);
}
auto CScreen::Clear() -> void
{
MT_PLATFORM_STUB();
if (ms_lpd3dDevice)
ms_lpd3dDevice->Clear(0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, ms_clearColor, ms_clearDepth, ms_clearStencil);
}
// 40250 GrpScreen.cpp:691. CPythonApplication::Process brackets the frame's render with Begin/End; only
@@ -76,9 +76,56 @@ LPDIRECT3D8 CPythonGraphic::GetD3D()
return &s_dummyD3D;
}
void CPythonGraphic::SetViewport(float x, float y, float width, float height) { UIRenderSetClip(x, y, width, height); }
void CPythonGraphic::RestoreViewport() { UIRenderRestoreClip(); }
void CPythonGraphic::SetOmniLight() { MT_PLATFORM_STUB(); }
void CPythonGraphic::SetViewport(float x, float y, float width, float height)
{
UIRenderSetClip(x, y, width, height);
}
void CPythonGraphic::RestoreViewport()
{
UIRenderRestoreClip();
}
void CPythonGraphic::SetOmniLight()
{
D3DMATERIAL8 Material;
Material.Ambient = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f);
Material.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
Material.Emissive = D3DXCOLOR(0.1f, 0.1f, 0.1f, 1.0f);
STATEMANAGER.SetMaterial(&Material);
D3DLIGHT8 Light;
Light.Type = D3DLIGHT_SPOT;
Light.Position = D3DXVECTOR3(50.0f, 150.0f, 350.0f);
Light.Direction = D3DXVECTOR3(-0.15f, -0.3f, -0.9f);
Light.Theta = D3DXToRadian(30.0f);
Light.Phi = D3DXToRadian(45.0f);
Light.Falloff = 1.0f;
Light.Attenuation0 = 0.0f;
Light.Attenuation1 = 0.005f;
Light.Attenuation2 = 0.0f;
Light.Diffuse.r = 1.0f;
Light.Diffuse.g = 1.0f;
Light.Diffuse.b = 1.0f;
Light.Diffuse.a = 1.0f;
Light.Ambient.r = 1.0f;
Light.Ambient.g = 1.0f;
Light.Ambient.b = 1.0f;
Light.Ambient.a = 1.0f;
Light.Range = 500.0f;
if (ms_lpd3dDevice)
{
ms_lpd3dDevice->SetLight(0, &Light);
ms_lpd3dDevice->LightEnable(0, TRUE);
Light.Type = D3DLIGHT_POINT;
Light.Position = D3DXVECTOR3(0.0f, 200.0f, 200.0f);
Light.Attenuation0 = 0.1f;
Light.Attenuation1 = 0.01f;
Light.Attenuation2 = 0.0f;
ms_lpd3dDevice->SetLight(1, &Light);
ms_lpd3dDevice->LightEnable(1, TRUE);
}
}
long CPythonGraphic::GenerateColor(float r, float g, float b, float a) { return GetColor(r, g, b, a); }
void CPythonGraphic::RenderUpButton(float, float, float, float) { MT_PLATFORM_STUB(); }
void CPythonGraphic::RenderDownButton(float, float, float, float) { MT_PLATFORM_STUB(); }
@@ -0,0 +1,23 @@
#include "AudioCommands.h"
#include <mutex>
namespace
{
std::mutex g_audio_mutex;
std::vector<AudioCommand> g_audio_commands;
}
void PushAudioCommand(const AudioCommand& cmd)
{
std::lock_guard<std::mutex> lock(g_audio_mutex);
g_audio_commands.push_back(cmd);
}
std::vector<AudioCommand> DrainAudioCommands()
{
std::lock_guard<std::mutex> lock(g_audio_mutex);
std::vector<AudioCommand> out;
out.swap(g_audio_commands);
return out;
}
@@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <vector>
struct AudioCommand
{
enum Type
{
PlaySound2D = 1,
PlaySound3D = 2,
StopSound3D = 3,
StopAllSound3D = 4,
PlayMusic = 5,
FadeOutMusic = 6,
FadeOutAllMusic = 7,
SetSoundVolume = 8,
SetMusicVolume = 9,
};
Type type = PlaySound2D;
std::string filename;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float volume = 1.0f;
float speed = 0.0f;
int play_count = 1;
int id = 0;
};
void PushAudioCommand(const AudioCommand& cmd);
std::vector<AudioCommand> DrainAudioCommands();
@@ -1,6 +1,7 @@
// Platform implementation of MilesLib/SoundManager.h for port/headless execution.
#include "MilesLib/Stdafx.h"
#include "MilesLib/SoundManager.h"
#include "AudioCommands.h"
#include <cmath>
#include <algorithm>
@@ -111,6 +112,10 @@ void CSoundManager::SetSoundVolume(float fVolume)
{
m_fBackupSoundVolume = fVolume;
}
AudioCommand cmd;
cmd.type = AudioCommand::SetSoundVolume;
cmd.volume = m_fSoundVolume;
PushAudioCommand(cmd);
}
void CSoundManager::SetSoundVolumeRatio(float fRatio)
@@ -141,6 +146,10 @@ void CSoundManager::__SetMusicVolume(float fVolume)
{
m_fBackupMusicVolume = fVolume;
}
AudioCommand cmd;
cmd.type = AudioCommand::SetMusicVolume;
cmd.volume = m_fMusicVolume;
PushAudioCommand(cmd);
}
void CSoundManager::SetMusicVolume(float fVolume)
@@ -184,25 +193,62 @@ float CSoundManager::GetMusicVolume()
return m_fMusicVolume;
}
void CSoundManager::PlaySound2D(const char *)
void CSoundManager::PlaySound2D(const char * c_szFileName)
{
if (m_isSoundDisable || !c_szFileName || !c_szFileName[0])
return;
AudioCommand cmd;
cmd.type = AudioCommand::PlaySound2D;
cmd.filename = c_szFileName;
cmd.volume = m_fSoundVolume;
PushAudioCommand(cmd);
}
void CSoundManager::PlaySound3D(float, float, float, const char *, int)
void CSoundManager::PlaySound3D(float fx, float fy, float fz, const char * c_szFileName, int iPlayCount)
{
if (m_isSoundDisable || !c_szFileName || !c_szFileName[0])
return;
AudioCommand cmd;
cmd.type = AudioCommand::PlaySound3D;
cmd.filename = c_szFileName;
cmd.x = fx;
cmd.y = fy;
cmd.z = fz;
cmd.play_count = iPlayCount;
cmd.volume = m_fSoundVolume;
PushAudioCommand(cmd);
}
void CSoundManager::StopSound3D(int)
void CSoundManager::StopSound3D(int iIndex)
{
AudioCommand cmd;
cmd.type = AudioCommand::StopSound3D;
cmd.id = iIndex;
PushAudioCommand(cmd);
}
int CSoundManager::PlayAmbienceSound3D(float, float, float, const char *, int)
int CSoundManager::PlayAmbienceSound3D(float fx, float fy, float fz, const char * c_szFileName, int iPlayCount)
{
return -1;
if (m_isSoundDisable || !c_szFileName || !c_szFileName[0])
return -1;
static int s_next_id = 1;
int id = s_next_id++;
AudioCommand cmd;
cmd.type = AudioCommand::PlaySound3D;
cmd.filename = c_szFileName;
cmd.x = fx;
cmd.y = fy;
cmd.z = fz;
cmd.play_count = iPlayCount;
cmd.volume = m_fSoundVolume;
cmd.id = id;
PushAudioCommand(cmd);
return id;
}
void CSoundManager::PlayCharacterSound3D(float, float, float, const char *, BOOL)
void CSoundManager::PlayCharacterSound3D(float fx, float fy, float fz, const char * c_szFileName, BOOL)
{
PlaySound3D(fx, fy, fz, c_szFileName, 1);
}
void CSoundManager::SetSoundVolume3D(int, float)
@@ -212,30 +258,51 @@ void CSoundManager::SetSoundVolume3D(int, float)
void CSoundManager::StopAllSound3D()
{
m_PlaySoundHistoryMap.clear();
AudioCommand cmd;
cmd.type = AudioCommand::StopAllSound3D;
PushAudioCommand(cmd);
}
void CSoundManager::PlayMusic(const char *)
void CSoundManager::PlayMusic(const char * c_szFileName)
{
if (!c_szFileName || !c_szFileName[0])
return;
AudioCommand cmd;
cmd.type = AudioCommand::PlayMusic;
cmd.filename = c_szFileName;
cmd.volume = m_fMusicVolume;
PushAudioCommand(cmd);
}
void CSoundManager::FadeInMusic(const char *, float)
void CSoundManager::FadeInMusic(const char * c_szFileName, float)
{
PlayMusic(c_szFileName);
}
void CSoundManager::FadeOutMusic(const char *, float)
void CSoundManager::FadeOutMusic(const char * c_szFileName, float fSpeed)
{
AudioCommand cmd;
cmd.type = AudioCommand::FadeOutMusic;
cmd.filename = c_szFileName ? c_szFileName : "";
cmd.speed = fSpeed;
PushAudioCommand(cmd);
}
void CSoundManager::FadeLimitOutMusic(const char *, float, float)
void CSoundManager::FadeLimitOutMusic(const char * c_szFileName, float, float fSpeed)
{
FadeOutMusic(c_szFileName, fSpeed);
}
void CSoundManager::FadeOutAllMusic()
{
AudioCommand cmd;
cmd.type = AudioCommand::FadeOutAllMusic;
PushAudioCommand(cmd);
}
void CSoundManager::FadeAll()
{
FadeOutAllMusic();
}
void CSoundManager::UpdateSoundData(DWORD, const NSound::TSoundDataVector *)
@@ -55,7 +55,8 @@ void CPythonApplication::Abort()
TraceError("Abort!!!!\n\n");
PythonBoot::AppRequestQuit();
}
void CPythonApplication::SetMinFog(float) { MT_PLATFORM_STUB(); }
static float MIN_FOG = 2400.0f;
void CPythonApplication::SetMinFog(float fMinFog) { MIN_FOG = fMinFog; }
// In 40250 Process() copies CTimer::GetCurrentSecond() into m_fGlobalTime right after Advance() and
// this returns it. PORT: no member to hold it; the timer only advances in Process(), so reading it
// here gives the same value.
@@ -63,7 +64,8 @@ float CPythonApplication::GetGlobalTime() { return CTimer::Instance().GetCurrent
// In 40250 Process() also copies CTimer::GetElapsedSecond() into m_fGlobalElapsedTime (PythonApplication.cpp:448).
// PORT: read straight from the timer, as GetGlobalTime above.
float CPythonApplication::GetGlobalElapsedTime() { return CTimer::Instance().GetElapsedSecond(); }
void CPythonApplication::SetFrameSkip(bool) { MT_PLATFORM_STUB(); }
static bool s_isFrameSkipDisable = false;
void CPythonApplication::SetFrameSkip(bool isEnable) { s_isFrameSkipDisable = !isEnable; }
// PORT: 40250 creates the Win32 window, the D3D8 device, cursors, sound, keyboard, the net device,
// fonts and the IME here. The Godot window and its canvas already exist when the host starts the
// script, so the window size is the canvas size (PythonBoot::SetUISize) rather than the width/height
@@ -386,9 +388,9 @@ void CPythonApplication::SetMouseHandler(PyObject* poMouseHandler)
s_poMouseHandler = poMouseHandler;
}
void CPythonApplication::SetGlobalCenterPosition(LONG, LONG) { MT_PLATFORM_STUB(); }
void CPythonApplication::SetFPS(int) { MT_PLATFORM_STUB(); }
time_t CPythonApplication::GetServerTime() { MT_PLATFORM_STUB(); return 0; }
time_t CPythonApplication::GetServerTimeStamp() { MT_PLATFORM_STUB(); return 0; }
void CPythonApplication::SetFPS(int) {}
time_t CPythonApplication::GetServerTime() { return time(nullptr); }
time_t CPythonApplication::GetServerTimeStamp() { return time(nullptr); }
void CPythonApplication::SetConnectData(const char*, int) { MT_PLATFORM_STUB(); }
void CPythonApplication::GetConnectData(std::string& ip, int& port) { MT_PLATFORM_STUB(); ip.clear(); port = 0; }
void CPythonApplication::SetForceSightRange(int) { MT_PLATFORM_STUB(); }
+32
View File
@@ -11,6 +11,7 @@
#include "platform/ScriptLib/PythonBoot.h"
#include "platform/EterLib/UIRenderCommands.h"
#include "platform/EterLib/RenderCommands3D.h"
#include "platform/MilesLib/AudioCommands.h"
#include <string>
#endif
@@ -244,6 +245,35 @@ Array Metin2PythonHost::render3d_draws() {
return out;
}
Array Metin2PythonHost::audio_commands() {
Array out;
for (const auto &cmd : DrainAudioCommands()) {
Dictionary item;
switch (cmd.type) {
case AudioCommand::PlaySound2D: item["type"] = "play_sound_2d"; break;
case AudioCommand::PlaySound3D: item["type"] = "play_sound_3d"; break;
case AudioCommand::StopSound3D: item["type"] = "stop_sound_3d"; break;
case AudioCommand::StopAllSound3D: item["type"] = "stop_all_sound_3d"; break;
case AudioCommand::PlayMusic: item["type"] = "play_music"; break;
case AudioCommand::FadeOutMusic: item["type"] = "fade_out_music"; break;
case AudioCommand::FadeOutAllMusic: item["type"] = "fade_out_all_music"; break;
case AudioCommand::SetSoundVolume: item["type"] = "set_sound_volume"; break;
case AudioCommand::SetMusicVolume: item["type"] = "set_music_volume"; break;
default: continue;
}
item["filename"] = String::utf8(cmd.filename.c_str());
item["x"] = cmd.x;
item["y"] = cmd.y;
item["z"] = cmd.z;
item["volume"] = cmd.volume;
item["speed"] = cmd.speed;
item["play_count"] = cmd.play_count;
item["id"] = cmd.id;
out.append(item);
}
return out;
}
String Metin2PythonHost::current_map_name() {
return String::utf8(PythonBoot::CurrentMapName().c_str());
}
@@ -285,6 +315,7 @@ void Metin2PythonHost::ui_update() {}
Array Metin2PythonHost::ui_render_commands() { return Array(); }
Ref<Image> Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref<Image>(); }
Array Metin2PythonHost::render3d_draws() { return Array(); }
Array Metin2PythonHost::audio_commands() { return Array(); }
String Metin2PythonHost::current_map_name() { return String(); }
void Metin2PythonHost::stop() {
@@ -312,6 +343,7 @@ void Metin2PythonHost::_bind_methods() {
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"),
&Metin2PythonHost::memory_texture, DEFVAL(-1));
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("render3d_draws"), &Metin2PythonHost::render3d_draws);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("audio_commands"), &Metin2PythonHost::audio_commands);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("current_map_name"), &Metin2PythonHost::current_map_name);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("stop"), &Metin2PythonHost::stop);
}
+1
View File
@@ -51,6 +51,7 @@ public:
// "view", "proj" (PackedFloat32Array, D3D row-vector layout), vertex arrays, "indices", "texture0"
// and the blend/depth/cull/lighting state.
static godot::Array render3d_draws();
static godot::Array audio_commands();
static godot::String current_map_name();
static void stop();