Skip to content

Commit d59b463

Browse files
committed
eeeeeeeee
1 parent 9b70b76 commit d59b463

File tree

5 files changed

+202
-17
lines changed

5 files changed

+202
-17
lines changed

source/backend/ClientPrefs.hx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,8 @@ class ClientPrefs {
459459
case "Light":
460460
yellow ? 'menuBG' : blue ? 'menuBGBlue' : 'menuDesat';
461461
case "Dark":
462-
Paths.assetLocation('menuDark').modded ? 'menuDark'
463-
: Paths.assetLocation('menuBG').modded ? (yellow ? 'menuBG' : blue ? 'menuBGBlue' : 'menuDesat')
462+
Paths.assetLocation('menuDark', null, null, null, true).modded ? 'menuDark'
463+
: Paths.assetLocation('menuBG', null, null, null, true).modded ? (yellow ? 'menuBG' : blue ? 'menuBGBlue' : 'menuDesat')
464464
: 'menuDark';
465465
default:
466466
FlxG.log.error("Invalid Menu Theme: " + ClientPrefs.data.menuTheme);

source/backend/Paths.hx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ class Paths
579579
// Similar to all functions, but instead this returns if it can find a modded file, or an asset file, as an object.
580580
// Example: Paths.assetLocation('images/character.png') will return the modded file if it exists, or the asset file if it doesn't.
581581
// Will return: {location: 'assets/images/character.png', modded: false} if the asset file exists, or {location: 'mods/character.png', modded: true} if the modded file exists.
582-
public static function assetLocation(key:String, ?parentFolder:String = null, ?pathType:PathType = IMAGES, ?allowNull:Bool = false):Null<{location:String, modded:Bool}>
582+
public static function assetLocation(key:String, ?parentFolder:String = null, ?pathType:PathType = IMAGES, ?allowNull:Bool = false, ?topModOnly:Bool = false):Null<{location:String, modded:Bool}>
583583
{
584584
var ext = switch (pathType) {
585585
case IMAGES: IMAGE_EXT;
@@ -629,22 +629,24 @@ class Paths
629629
return {location: modPath, modded: true};
630630
}
631631
}
632-
for (mod in Mods.parseList().enabled) {
633-
var modPath = 'mods/$mod/$filePath';
634-
if (FileSystem.exists(modPath)) {
635-
return {location: modPath, modded: true};
632+
if (!topModOnly) {
633+
for (mod in Mods.parseList().enabled) {
634+
var modPath = 'mods/$mod/$filePath';
635+
if (FileSystem.exists(modPath)) {
636+
return {location: modPath, modded: true};
637+
}
636638
}
637-
}
638-
for (mod in Mods.getGlobalMods()) {
639-
var modPath = 'mods/$mod/$filePath';
640-
if (FileSystem.exists(modPath)) {
641-
return {location: modPath, modded: true};
639+
for (mod in Mods.getGlobalMods()) {
640+
var modPath = 'mods/$mod/$filePath';
641+
if (FileSystem.exists(modPath)) {
642+
return {location: modPath, modded: true};
643+
}
644+
}
645+
// Check base mods folder (for loose files)
646+
var looseModPath = 'mods/$filePath';
647+
if (FileSystem.exists(looseModPath)) {
648+
return {location: looseModPath, modded: true};
642649
}
643-
}
644-
// Check base mods folder (for loose files)
645-
var looseModPath = 'mods/$filePath';
646-
if (FileSystem.exists(looseModPath)) {
647-
return {location: looseModPath, modded: true};
648650
}
649651
#end
650652

source/import.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,5 @@ using yutautil.PointerTools;
101101
using yutautil.CUMacroTools; // Careful. Using C++ Lables in Haxe may act strangely.
102102
using yutautil.KonamiTracker;
103103
using yutautil.GenericObject;
104+
using yutautil.PyScript;
104105
#end

source/yutautil/PyScript.hx

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
2+
package yutautil;
3+
4+
#if cpp
5+
import cpp.Lib;
6+
import cpp.Pointer;
7+
import cpp.Native;
8+
import haxe.Json;
9+
import sys.io.File;
10+
11+
class Pycript {
12+
// Python version info
13+
public static var pythonVersion(default, null):String;
14+
public static var pythonInitialized(default, null):Bool = false;
15+
16+
// Script management
17+
private var moduleName:String;
18+
private var module:Dynamic;
19+
private var callbacks:Map<String, String> = new Map();
20+
private var transformCallbackNames:Bool = true;
21+
22+
// Initialize Python interpreter
23+
public static function initPython() {
24+
if (pythonInitialized) return;
25+
26+
untyped __cpp__("
27+
Py_Initialize();
28+
PyEval_InitThreads(); // Initialize and acquire GIL
29+
");
30+
31+
// Get Python version
32+
untyped __cpp__("
33+
char version[128];
34+
snprintf(version, sizeof(version), \"Python %d.%d.%d\",
35+
PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION);
36+
");
37+
pythonVersion = untyped __cpp__('version');
38+
39+
pythonInitialized = true;
40+
trace('Python initialized: $pythonVersion');
41+
}
42+
43+
// Constructor - Loads Python script
44+
public function new(scriptPath:String, moduleName:String) {
45+
if (!pythonInitialized) initPython();
46+
this.moduleName = moduleName;
47+
48+
// Execute script in isolated module
49+
var scriptContent = File.getContent(scriptPath);
50+
untyped __cpp__('
51+
// Create new module
52+
PyObject *module = PyModule_New("{0}");
53+
PyModule_AddStringConstant(module, "__file__", "{1}");
54+
55+
// Add to sys.modules
56+
PyObject *sys_modules = PyImport_GetModuleDict();
57+
PyDict_SetItemString(sys_modules, "{0}", module);
58+
59+
// Execute script in module context
60+
PyObject *globals = PyModule_GetDict(module);
61+
PyRun_StringFlags(
62+
{2}, Py_file_input,
63+
globals, globals, NULL
64+
);
65+
66+
// Store module reference
67+
{3} = module;
68+
', moduleName, scriptPath, scriptContent, cpp.Pointer.addressOf(module));
69+
70+
// Call main function if exists
71+
if (functionExists("main")) {
72+
callFunction("main", []);
73+
}
74+
}
75+
76+
// Set callback name transformation (onNoteHit -> on_note_hit)
77+
public function setTransformCallbackNames(transform:Bool) {
78+
transformCallbackNames = transform;
79+
}
80+
81+
// Expose Haxe object to Python
82+
public function expose(name:String, obj:Dynamic) {
83+
var json = Json.stringify(obj);
84+
untyped __cpp__('
85+
PyObject *pyObj = PyUnicode_FromString({0});
86+
PyObject *module = {1};
87+
PyObject *dict = PyModule_GetDict(module);
88+
PyDict_SetItemString(dict, {2}, pyObj);
89+
Py_DECREF(pyObj);
90+
', json, module, name);
91+
}
92+
93+
// Call Python function
94+
public function callFunction(funcName:String, args:Array<Dynamic>):Dynamic {
95+
if (!functionExists(funcName)) return null;
96+
97+
var jsonArgs = Json.stringify(args);
98+
var result:Dynamic = null;
99+
100+
untyped __cpp__('
101+
PyGILState_STATE gstate = PyGILState_Ensure();
102+
103+
try {
104+
PyObject *module = {0};
105+
PyObject *func = PyObject_GetAttrString(module, {1});
106+
107+
if (func && PyCallable_Check(func)) {
108+
// Convert Haxe args to Python tuple
109+
PyObject *pyArgs = PyTuple_New({2});
110+
for (int i = 0; i < {2}; i++) {
111+
PyObject *item = PyUnicode_FromString({3}[i]);
112+
PyTuple_SetItem(pyArgs, i, item);
113+
}
114+
115+
// Call function
116+
PyObject *pyResult = PyObject_CallObject(func, pyArgs);
117+
118+
// Convert result to JSON string
119+
if (pyResult) {
120+
PyObject *json = PyObject_CallMethod(pyResult, "__str__", NULL);
121+
{4} = PyUnicode_AsUTF8(json);
122+
Py_DECREF(json);
123+
Py_DECREF(pyResult);
124+
}
125+
126+
Py_DECREF(pyArgs);
127+
Py_DECREF(func);
128+
}
129+
} catch (...) {
130+
PyErr_Print();
131+
}
132+
133+
PyGILState_Release(gstate);
134+
', module, funcName, args.length, jsonArgs, cpp.Pointer.addressOf(result));
135+
136+
try {
137+
return Json.parse(result);
138+
} catch (e:Dynamic) {
139+
return result;
140+
}
141+
}
142+
143+
// Register callback handler
144+
public function registerCallback(eventName:String, ?pyFunctionName:String) {
145+
if (pyFunctionName == null) {
146+
pyFunctionName = transformCallbackNames ?
147+
StringTools.replace(eventName, "on", "on_").toLowerCase() :
148+
eventName;
149+
}
150+
callbacks.set(eventName, pyFunctionName);
151+
}
152+
153+
// Trigger callback
154+
public function triggerCallback(eventName:String, args:Array<Dynamic>) {
155+
var funcName = callbacks.get(eventName);
156+
if (funcName != null && functionExists(funcName)) {
157+
callFunction(funcName, args);
158+
}
159+
}
160+
161+
// Check if function exists
162+
public function functionExists(funcName:String):Bool {
163+
var exists = false;
164+
untyped __cpp__('
165+
PyObject *module = {0};
166+
exists = (PyObject_HasAttrString(module, {1}) == 1;
167+
', module, funcName, cpp.Pointer.addressOf(exists));
168+
return exists;
169+
}
170+
171+
// Clean up resources
172+
public function destroy() {
173+
untyped __cpp__('
174+
PyGILState_STATE gstate = PyGILState_Ensure();
175+
Py_DECREF({0});
176+
PyGILState_Release(gstate);
177+
', module);
178+
}
179+
}
180+
#end

source/yutautil/Valid.hx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package yutautil;
22

33
// Abstract for a "Valid" type that can be used as a boolean or as a conditional tree
44

5+
6+
typedef ComplexBool = Valid;
57
private class ValidImpl {
68
public var expr: () -> Bool;
79
public var ifTree: ValidIfTree;

0 commit comments

Comments
 (0)