Compare commits

...

2 Commits

Author SHA1 Message Date
f88e9ce7d0 cleanup and 32 bit fallback 2026-08-06 00:40:20 +02:00
fedc18c3ca WIP 2026-08-06 00:29:37 +02:00
19 changed files with 783 additions and 134 deletions

24
hGameTest/Makefile Normal file
View File

@@ -0,0 +1,24 @@
# Makefile for standalone OpenFL application
# Default target
.PHONY: all
all: build
# Build the application
.PHONY: build
build:
haxe build.hxml
# Run the compiled application
.PHONY: run
run:
bash -c "cd bin/cpp && ./ApplicationMain"
# Build and run the application
.PHONY: test
test: build run
# Clean build artifacts
.PHONY: clean
clean:
rm -rf bin/cpp

View File

@@ -17,7 +17,8 @@ class Project extends HXProject {
// App settings // App settings
app.main = "Main"; app.main = "Main";
app.file = "hgame"; app.file = "hgame";
app.path = "bin"; app.path = "bin/openfl-build-chain";
app.preloader = "HGamePreloader";
// Window config // Window config
@@ -52,6 +53,8 @@ class Project extends HXProject {
haxeflags.push("--macro keep('Main')"); haxeflags.push("--macro keep('Main')");
haxeflags.push("-dce full"); haxeflags.push("-dce full");
// target = Platform.LINUX; target = Platform.LINUX;
} }
} }

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1 @@
This file is intentionally left blank.

View File

@@ -0,0 +1,18 @@
{
"dataScheme":"textureList",
"version":1,
"textureData": {
"spriteSheets":[{
"sheetName":"testSheet",
"path":"sheets/sheet.png",
"sheetDataPath":"sheets/sheet.json",
"explicitDimensions":true,
"tileDimensions":{"width":16,"height":16}
}],
"sprites":
[{
"spriteName":"player",
"path":"individual/character.png"
}]
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

View File

@@ -1,9 +1,31 @@
-lib actuate -main ApplicationMain
-lib lime
-lib openfl -lib openfl
-lib hxp -lib lime
-lib actuate
-lib hxcpp-debug-server
--main ApplicationMain
-cp src -cp src
-D lime-harfbuzz
--cpp bin/cpp -D lime-openal
-D lime-threads
-D tools=8.2.2
-D lime-cairo
-D lime-opengl
# -D no-compilation
-D native
-D lime-curl
-D lime-native
-D lime-vorbis
-D openfl-native
-D lime-cffi
-D linux
-D desktop
-D debug
--remap flash:openfl
-cpp bin/cpp
--macro keep("Main")
--macro keep("Assets")
--macro keep("AssetMacro")
--macro addGlobalMetadata('', '@:build(engine.macros.ConVarDecorators.build())')
# --macro macros.MacroLoader.run()

View File

@@ -1,26 +1,39 @@
/*
* Copyright (c) 2025 ${company}
* Author: ${author}
* License: ${license}
*/
// TODO: kanker
package; package;
import openfl.events.Event;
#if macro #if macro
import haxe.macro.Compiler; import haxe.macro.Compiler;
import haxe.macro.Context; import haxe.macro.Context;
import haxe.macro.Expr; import haxe.macro.Expr;
#end #end
@:access(lime.app.Application) @:access(lime.app.Application)
@:access(lime.system.System) @:access(lime.system.System)
@:access(openfl.display.Stage) @:access(openfl.display.Stage)
@:access(openfl.events.UncaughtErrorEvents) @:access(openfl.events.UncaughtErrorEvents)
@:dox(hide) @:dox(hide)
class ApplicationMain class ApplicationMain
{ {
/**
* [Description]
*
*/
#if !macro #if !macro
public static function main() public static function main()
{ {
lime.system.System.__registerEntryPoint("::APP_FILE::", create); lime.system.System.__registerEntryPoint("MinimalApplication", create);
#if (js && html5) #if (js && html5)
#if (munit || (utest && openfl_enable_utest_legacy_mode)) #if (munit || (utest && openfl_enable_utest_legacy_mode))
lime.system.System.embed("::APP_FILE::", null, ::WIN_WIDTH::, ::WIN_HEIGHT::); lime.system.System.embed("MinimalApplication", null, 800, 600);
#end #end
#else #else
create(null); create(null);
@@ -35,49 +48,48 @@ class ApplicationMain
ManifestResources.init(config); ManifestResources.init(config);
#end #end
app.meta["build"] = "::meta.buildNumber::";
app.meta["company"] = "::meta.company::";
app.meta["file"] = "::APP_FILE::";
app.meta["name"] = "::meta.title::";
app.meta["packageName"] = "::meta.packageName::";
app.meta["version"] = "::meta.version::";
::if (config.hxtelemetry != null)::#if hxtelemetry
app.meta["hxtelemetry-allocations"] = "::config.hxtelemetry.allocations::"; app.meta["build"] = "48";
app.meta["hxtelemetry-host"] = "::config.hxtelemetry.host::"; app.meta["company"] = "Company Name";
#end::end:: app.meta["file"] = "MinimalApplication";
app.meta["name"] = "MinimalApplication";
app.meta["packageName"] = "com.sample.minimalapplication";
app.meta["version"] = "1.0.0";
#if !flash #if !flash
::foreach windows::
var attributes:lime.ui.WindowAttributes = { var attributes:lime.ui.WindowAttributes = {
allowHighDPI: ::allowHighDPI::, allowHighDPI: false,
alwaysOnTop: ::alwaysOnTop::, alwaysOnTop: false,
borderless: ::borderless::, borderless: false,
// display: ::display::, // display: 0,
element: null, element: null,
frameRate: ::fps::, frameRate: 30,
#if !web fullscreen: ::fullscreen::, #end #if !web fullscreen: false, #end
height: ::height::, height: 600,
hidden: #if munit true #else ::hidden:: #end, hidden: #if munit true #else false #end,
maximized: ::maximized::, maximized: false,
minimized: ::minimized::, minimized: false,
parameters: ::parameters::, parameters: {},
resizable: ::resizable::, resizable: true,
title: "::title::", title: "MinimalApplication",
width: ::width::, width: 800,
x: ::x::, x: null,
y: ::y::, y: null,
}; };
attributes.context = { attributes.context = {
antialiasing: ::antialiasing::, antialiasing: 0,
background: ::background::, background: 16777215,
colorDepth: ::colorDepth::, colorDepth: 32,
depth: ::depthBuffer::, depth: true,
hardware: ::hardware::, hardware: true,
stencil: ::stencilBuffer::, stencil: true,
type: null, type: null,
vsync: ::vsync:: vsync: false
}; };
if (app.window == null) if (app.window == null)
@@ -103,14 +115,15 @@ class ApplicationMain
} }
app.createWindow(attributes); app.createWindow(attributes);
::end::
#elseif air #elseif air
app.window.title = "::meta.title::"; app.window.title = "MinimalApplication";
#else #else
app.window.context.attributes.background = ::WIN_BACKGROUND::; app.window.context.attributes.background = 16777215;
app.window.frameRate = ::WIN_FPS::; app.window.frameRate = 30;
#end #end
var preloader = getPreloader(); var preloader = getPreloader();
app.preloader.onProgress.add (function(loaded, total) app.preloader.onProgress.add (function(loaded, total)
{ {
@@ -120,7 +133,6 @@ class ApplicationMain
{ {
@:privateAccess preloader.start(); @:privateAccess preloader.start();
}); });
preloader.onComplete.add(start.bind((cast app.window:openfl.display.Window).stage)); preloader.onComplete.add(start.bind((cast app.window:openfl.display.Window).stage));
#if !disable_preloader_assets #if !disable_preloader_assets
@@ -188,7 +200,7 @@ class ApplicationMain
{ {
var hasMain = false; var hasMain = false;
switch (Context.follow(Context.getType("::APP_MAIN::"))) switch (Context.follow(Context.getType("Main")))
{ {
case TInst(t, params): case TInst(t, params):
@@ -204,7 +216,7 @@ class ApplicationMain
if (hasMain) if (hasMain)
{ {
return Context.parse("@:privateAccess ::APP_MAIN::.main()", Context.currentPos()); return Context.parse("@:privateAccess Main.main()", Context.currentPos());
} }
else if (type.constructor != null) else if (type.constructor != null)
{ {
@@ -223,12 +235,12 @@ class ApplicationMain
} }
else else
{ {
Context.fatalError("Main class \"::APP_MAIN::\" has neither a static main nor a constructor.", Context.currentPos()); Context.fatalError("Main class \"Main\" has neither a static main nor a constructor.", Context.currentPos());
} }
default: default:
Context.fatalError("Main class \"::APP_MAIN::\" isn't a class.", Context.currentPos()); Context.fatalError("Main class \"Main\" isn't a class.", Context.currentPos());
} }
return null; return null;
@@ -236,8 +248,8 @@ class ApplicationMain
macro public static function getPreloader() macro public static function getPreloader()
{ {
::if (PRELOADER_NAME != "")::
var type = Context.getType("::PRELOADER_NAME::"); var type = Context.getType("HGamePreloader");
switch (type) switch (type)
{ {
@@ -251,7 +263,7 @@ class ApplicationMain
{ {
return macro return macro
{ {
new ::PRELOADER_NAME::(); new HGamePreloader();
}; };
} }
@@ -270,16 +282,12 @@ class ApplicationMain
return macro return macro
{ {
new openfl.display.Preloader(new ::PRELOADER_NAME::()); new openfl.display.Preloader(new HGamePreloader());
} }
::else::
return macro
{
new openfl.display.Preloader(new openfl.display.Preloader.DefaultPreloader());
};
::end::
} }
#if !macro #if !macro
@:noCompletion @:dox(hide) public static function __init__() @:noCompletion @:dox(hide) public static function __init__()
{ {
@@ -326,7 +334,7 @@ class ApplicationMain
#if !macro #if !macro
@:build(DocumentClass.build()) @:build(DocumentClass.build())
@:keep @:dox(hide) class DocumentClass extends ::APP_MAIN:: {} @:keep @:dox(hide) class DocumentClass extends Main {}
#else #else
class DocumentClass class DocumentClass
{ {

29
hGameTest/src/Assets.hx Normal file
View File

@@ -0,0 +1,29 @@
package;
/**
* Asset definition class using meta tags instead of project.xml
* The AssetMacro will process these declarations at compile time
*/
@:build(macros.AssetMacro.build()) // Fix: use fully qualified path with package name
@:keep
class Assets {
// Define image assets
@:Assets("assets/images/", "images")
private static var images:String;
// Define sound assets
@:Assets("assets/sounds/", "sounds")
private static var sounds:String;
// Define font assets
@:Assets("assets/fonts/", "fonts")
private static var fonts:String;
// Define data assets
@:Assets("assets/data/", "data")
private static var data:String;
// You can also define individual files if needed
// @:Assets("assets/images/logo.png", "images", "logo.png")
// public static var logo:String;
}

View File

@@ -0,0 +1,200 @@
package;
import openfl.events.ProgressEvent;
import openfl.Lib;
import haxe.Timer;
import openfl.events.KeyboardEvent;
import openfl.text.TextField;
import openfl.display.Preloader;
import openfl.events.Event;
import openfl.display.Sprite;
@:keep @:noCompletion #if display private #end class __ASSET__fonts_terminus_ttf extends lime.text.Font {
public function new() {
__fontPath = ManifestResources.rootPath + "fonts/Terminus.ttf";
name = "Terminus (TTF)";
super();
}
}
class HGamePreloader extends Sprite {
public static var preloadScreen:HGamePreloader;
private var textField:TextField;
public function new() {
super();
init();
initUI();
// openfl.Lib.current.addChild(preloadScreen);
trace("Preloader initialized");
textField.text = "Preloader ...";
this.addEventListener(Event.COMPLETE, onComplete);
this.addEventListener(ProgressEvent.PROGRESS, (e:ProgressEvent) -> {
trace(e.bytesLoaded + "/" + e.bytesTotal);
onUpdate(cast e.bytesLoaded, cast e.bytesTotal);
});
Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onDismiss);
}
public function onComplete(event:Event) {
event.preventDefault();
trace("Preloader complete");
}
public function pltrace(msg:String) {
trace(msg);
if (textField != null) {
textField.text += "\n" + msg;
}
}
public var initialized:Bool = false;
public function init() {
// Preload assets
trace("Preloading assets...");
registerFonts();
// Add your asset loading logic here
// For example, you can load images, sounds, etc.
// You can use openfl.Assets.loadBitmapData() or openfl.Assets.loadSound() to load assets
// and then add them to the preloadScreen or do any other processing you need.
// Require clicking to start the game
// Add an event listener to the stage to check for input
initialized = true;
}
public function onUpdate(loaded:Int, total:Int) {
trace("Preloader update: " + loaded + "/" + total);
// Update the preloader screen
// You can update the progress bar or any other UI elements here
// For example, you can use textField.text to show loading progress
// textField.text = "Loading... " + Math.round(progress * 100) + "%";
if (textField != null) {
textField.text = "Loading... " + Math.round(loaded / total * 100) + "%";
}
// You can also update the preloader screen's graphics or animations here
// For example, you can change the color or size of the preloader screen
preloadScreen.graphics.clear();
preloadScreen.graphics.beginFill(Math.random() * 0xFFFFFF);
preloadScreen.graphics.drawRect(0, 0, 800, 600);
preloadScreen.graphics.endFill();
}
public function registerFonts() {
@:privateAccess
var fontLibrary = openfl.utils.Assets.getLibrary("fonts");
if (fontLibrary == null) {
trace("Font library not found");
return;
}
var fontAssets:Array<Dynamic> = fontLibrary.list('FONT');
if (fontAssets == null) {
trace("Font assets not found");
return;
}
for (fontAsset in fontAssets) {
trace('fontAsset: ${fontAsset}');
var font = fontLibrary.getFont(fontAsset);
if (font == null) {
trace("Font not found: " + fontAsset);
continue;
}
trace(@:privateAccess font.__fontPath);
openfl.text.Font.registerFont(font);
trace("Font registered: " + fontAsset);
}
}
public function onDismiss(event:KeyboardEvent) {
trace("Preloader dismissed");
if (preloadScreen == null || openfl.Lib.current == null)
return;
if (event.keyCode != openfl.ui.Keyboard.ENTER)
return this.removeEventListener(KeyboardEvent.KEY_UP, onDismiss);
openfl.Lib.current.removeChild(preloadScreen);
// var app = ApplicationMain.app;
dispatchEvent(new Event(Event.UNLOAD));
}
public function initUI() {
// Initialize the UI elements
trace("Initializing UI...");
// You can add your UI initialization logic here
// For example, you can create buttons, sliders, etc.
// and add them to the preloadScreen or any other container
preloadScreen = this;
preloadScreen.graphics.beginFill(0x000000);
preloadScreen.graphics.drawRect(0, 0, 800, 600);
preloadScreen.graphics.endFill();
preloadScreen.x = 0;
preloadScreen.y = 0;
preloadScreen.alpha = 0.5;
preloadScreen.scaleX = 1;
preloadScreen.scaleY = 1;
preloadScreen.mouseEnabled = false;
preloadScreen.mouseChildren = false;
preloadScreen.name = "preloadScreen";
preloadScreen.visible = true;
preloadScreen.cacheAsBitmap = true;
preloadScreen.filters = [];
preloadScreen.transform.colorTransform = new openfl.geom.ColorTransform(1, 1, 1, 1, 0, 0, 0, 0);
preloadScreen.transform.matrix = new openfl.geom.Matrix(1, 0, 0, 1, 0, 0);
preloadScreen.transform.matrix3D = new openfl.geom.Matrix3D();
textField = new openfl.text.TextField();
textField.x = 10;
textField.y = 10;
textField.width = 780;
textField.height = 580;
textField.multiline = true;
textField.wordWrap = true;
textField.selectable = false;
textField.autoSize = openfl.text.TextFieldAutoSize.LEFT;
textField.defaultTextFormat = new openfl.text.TextFormat("Arial", 12, 0xFFFFFF);
textField.embedFonts = true;
textField.antiAliasType = openfl.text.AntiAliasType.ADVANCED;
textField.background = true;
textField.backgroundColor = 0x000000;
textField.border = true;
textField.borderColor = 0xFFFFFF;
textField.textColor = 0xFFFFFF;
textField.text = "Loading...";
textField.alpha = 1;
textField.scaleX = 1;
textField.scaleY = 1;
textField.mouseEnabled = false;
textField.name = "textField";
textField.visible = true;
textField.cacheAsBitmap = true;
textField.filters = [];
textField.transform.colorTransform = new openfl.geom.ColorTransform(1, 1, 1, 1, 0, 0, 0, 0);
textField.transform.matrix = new openfl.geom.Matrix(1, 0, 0, 1, 0, 0);
textField.transform.matrix3D = new openfl.geom.Matrix3D();
preloadScreen.addChild(textField);
}
public function onKeyDown(event:KeyboardEvent) {
trace("Key pressed: " + event.keyCode);
if (!initialized)
return;
if (event.keyCode == openfl.ui.Keyboard.ENTER) {
// Start the game
trace("Starting game...");
openfl.Lib.current.stage.removeChild(preloadScreen);
}
}
}

View File

@@ -1,13 +1,8 @@
import openfl.display.Window;
import assets.Scanner;
import game.video.Mode;
import openfl.events.KeyboardEvent; import openfl.events.KeyboardEvent;
import openfl.display.Sprite; import openfl.display.Sprite;
import openfl.display.Stage;
import openfl.events.Event; import openfl.events.Event;
import game.Input; import game.Input;
import game.Game; import game.Game;
import game.video.Mode;
class Main extends Sprite { class Main extends Sprite {
public function new () { public function new () {
@@ -24,26 +19,13 @@ class Main extends Sprite {
stage.addEventListener(KeyboardEvent.KEY_UP,Input.onKeyIsUp); stage.addEventListener(KeyboardEvent.KEY_UP,Input.onKeyIsUp);
stage.addEventListener(Event.RESIZE,onResize); stage.addEventListener(Event.RESIZE,onResize);
game.onInit(); game.onInit();
//Mode.setVideoMode(1280, 960);
} }
private function onResize (event:Event):Void { private function onResize (event:Event):Void {
//Here we can do shit with window scaling //Here we can do shit with window scaling
//stage.stageWidth; //stage.stageWidth;
//stage.stageHeight; //stage.stageHeight;
} }
// static function main () {
// // Bootstrap the OpenFL project
// var application = new lime.app.Application();
// application.createWindow({
// hidden: false,
// x: 0,
// y: 0,
// width: 800,
// height: 600,
// title: "DSTEngine Window"
// });
// }
} }

View File

@@ -0,0 +1,61 @@
package;
#if sys
import haxe.io.Path;
import openfl.utils.AssetLibrary;
import openfl.utils.AssetManifest;
import openfl.utils.Assets as LimeAssets;
import sys.FileSystem;
@:access(lime.utils.Assets)
@:keep class ManifestResources {
public static var preloadLibraries:Array<AssetLibrary> = [];
public static var preloadLibraryNames:Array<String> = [];
public static var rootPath:String;
public static function init(config:Dynamic):Void {
loadManifests();
}
public static function loadManifests() {
rootPath = "";
var manifestDirPath = Path.join(["./manifest"]);
var manifestFiles = FileSystem.readDirectory(manifestDirPath);
for (manifestFile in manifestFiles) {
var manifestPath = Path.join([manifestDirPath, manifestFile]);
if (!StringTools.endsWith(manifestFile, ".json")){
trace('${manifestFile} is not a json file, skipping...');
continue;
}
loadManifest(manifestPath, manifestFile.split(".json")[0]);
}
}
public static function loadManifest(manifestPath, manifestName) {
if (!FileSystem.exists(manifestPath)) {
trace("Manifest file not found: " + manifestPath);
return;
}
try {
var manifest = AssetManifest.fromFile(manifestPath);
if (manifest != null) {
var library = AssetLibrary.fromManifest(manifest);
if (library != null) {
trace("Library: `"+library+"`");
trace("manifestName: `"+manifestName+"`");
preloadLibraries.push(library);
preloadLibraryNames.push(manifestName);
// LimeAssets.registerLibrary(manifestName, library);
openfl.utils.Assets.registerLibrary(manifestName, library);
}
}
} catch (e:Dynamic) {
trace("Error loading asset manifest: " + e);
}
}
}
#end

View File

@@ -10,8 +10,16 @@ class TextFormats{
public var cInputFmt:TextFormat; public var cInputFmt:TextFormat;
public var cOutputFmt:TextFormat; public var cOutputFmt:TextFormat;
public function new(){ public function new(){
var font = Assets.getFont("fonts/Terminus.ttf");
cInputFmt = new TextFormat(Assets.getFont("fonts/Terminus.ttf").fontName, 24, 0xffffff); cInputFmt = new TextFormat(Assets.getFont("fonts/Terminus.ttf").fontName, 24, 0xffffff);
cOutputFmt = new TextFormat(Assets.getFont("fonts/Terminus.ttf").fontName, 24, 0xffffff); cOutputFmt = new TextFormat(Assets.getFont("fonts/Terminus.ttf").fontName, 24, 0xffffff);
trace('font: ${font}');
trace('fontName: ${font.fontName}');
trace('fontStyle: ${font.fontStyle}');
trace('fontType: ${font.fontType}');
trace('fontGlyphs: ${font.getGlyphs()}');
trace(cInputFmt.font);
trace(cOutputFmt.font);
} }
public static function getFormats():TextFormats public static function getFormats():TextFormats
{ {

View File

@@ -0,0 +1,269 @@
package macros;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
import sys.FileSystem;
import sys.io.File;
import haxe.Json;
using StringTools;
/**
* Macro for processing @:Assets meta tags and building asset manifests
*/
@:keep
class AssetMacro {
// Track all assets processed
private static var assetMap:Map<String, Array<{id:String, path:String, size:Int, type:String}>> = new Map();
/**
* Entry point for the macro - called during compilation
*/
public static function build():Array<Field> {
#if macro
Sys.println("AssetMacro.build() called - macro is running");
Context.warning("AssetMacro is running", Context.currentPos());
#end
// Get current build context
var fields = Context.getBuildFields();
var localClass = Context.getLocalClass().get();
#if macro
Sys.println("Processing class: " + localClass.name);
#end
// Get the output directory from build.hxml
var outputDir = getOutputDir();
var manifestDir = outputDir + "/manifest";
var assetsOutputDir = outputDir + "/assets";
// Create directories if they don't exist
createDirectory(manifestDir);
createDirectory(assetsOutputDir);
// Process asset fields
for (field in fields) {
processField(field, assetsOutputDir);
}
// Build the manifest files
buildManifests(manifestDir);
return fields;
}
/**
* Process a field for @:Assets meta
*/
private static function processField(field:Field, assetsOutputDir:String) {
// Look for our custom meta
var assetsMeta = findAssetsMeta(field.meta);
if (assetsMeta == null) return;
var params = assetsMeta.params;
if (params.length < 2) {
Context.error("@:Assets meta requires at least 2 parameters: source path and library name", assetsMeta.pos);
return;
}
// Extract parameters
var sourcePath = extractString(params[0]);
var libraryName = extractString(params[1]);
var targetPath = params.length > 2 ? extractString(params[2]) : null;
// Process directory or single file
if (FileSystem.exists(sourcePath)) {
if (FileSystem.isDirectory(sourcePath)) {
processDirectory(sourcePath, libraryName, assetsOutputDir);
} else {
processSingleFile(sourcePath, libraryName, targetPath, assetsOutputDir);
}
} else {
Context.warning('Asset source not found: ${sourcePath}', field.pos);
}
}
/**
* Process a directory of assets
*/
private static function processDirectory(sourcePath:String, libraryName:String, assetsOutputDir:String) {
if (!sourcePath.endsWith("/")) sourcePath += "/";
var targetDir = assetsOutputDir + "/" + libraryName + "/";
createDirectory(targetDir);
function scanDir(path:String, relPath:String = "") {
if (!FileSystem.exists(path)) return;
for (entry in FileSystem.readDirectory(path)) {
var fullPath = path + entry;
var relAssetPath = relPath + entry;
if (FileSystem.isDirectory(fullPath)) {
var newRelPath = relPath + entry + "/";
var newTargetDir = targetDir + newRelPath;
createDirectory(newTargetDir);
scanDir(fullPath + "/", newRelPath);
} else {
// Copy file and register in asset map
var targetPath = targetDir + relAssetPath;
try {
File.copy(fullPath, targetPath);
// Register asset
if (!assetMap.exists(libraryName)) {
assetMap.set(libraryName, []);
}
assetMap.get(libraryName).push({
id: relAssetPath,
path: relAssetPath,
size: FileSystem.stat(fullPath).size,
type: getAssetType(fullPath)
});
} catch (e) {
Context.warning('Failed to copy asset: ${fullPath} -> ${targetPath}. Error: ${e}', Context.currentPos());
}
}
}
}
scanDir(sourcePath);
}
/**
* Process a single file asset
*/
private static function processSingleFile(sourcePath:String, libraryName:String, targetPath:String, assetsOutputDir:String) {
var targetDir = assetsOutputDir + "/" + libraryName + "/";
createDirectory(targetDir);
var fileName = targetPath != null ? targetPath : sourcePath.split("/").pop();
var destPath = targetDir + fileName;
try {
File.copy(sourcePath, destPath);
// Register asset
if (!assetMap.exists(libraryName)) {
assetMap.set(libraryName, []);
}
assetMap.get(libraryName).push({
id: fileName,
path: fileName,
size: FileSystem.stat(sourcePath).size,
type: getAssetType(sourcePath)
});
} catch (e) {
Context.warning('Failed to copy asset: ${sourcePath} -> ${destPath}. Error: ${e}', Context.currentPos());
}
}
/**
* Build manifest JSON files for each library
*/
private static function buildManifests(manifestDir:String) {
// Create an array to hold all assets for the default manifest
var allAssets = [];
// First, process individual library manifests
for (library in assetMap.keys()) {
var assets = assetMap.get(library);
var manifest = {
name: library,
assets: assets,
rootPath: "../assets/" + library + "/"
};
// Add assets to combined collection with library prefix
for (asset in assets) {
allAssets.push({
id: library + "/" + asset.id,
path: library + "/" + asset.path,
size: asset.size,
type: asset.type
});
}
var manifestPath = manifestDir + "/" + library + ".json";
try {
var content = Json.stringify(manifest, null, " ");
File.saveContent(manifestPath, content);
Context.info('Built manifest: ${manifestPath} with ${assets.length} assets', Context.currentPos());
} catch (e) {
Context.warning('Failed to write manifest: ${manifestPath}. Error: ${e}', Context.currentPos());
}
}
// Create the default manifest with all assets
var defaultManifest = {
name: "default",
assets: allAssets,
rootPath: "../assets/"
};
// Save the default manifest
var defaultManifestPath = manifestDir + "/default.json";
try {
var content = Json.stringify(defaultManifest, null, " ");
File.saveContent(defaultManifestPath, content);
Context.info('Built default manifest: ${defaultManifestPath} with ${allAssets.length} assets', Context.currentPos());
} catch (e) {
Context.warning('Failed to write default manifest: ${defaultManifestPath}. Error: ${e}', Context.currentPos());
}
}
// Helper functions
private static function getOutputDir():String {
// Try to parse the --cpp argument from the build.hxml
var args = Sys.args();
for (i in 0...args.length) {
if (args[i] == "--cpp" && i < args.length - 1) {
return args[i + 1];
}
}
return "bin/cpp"; // Default output directory
}
private static function createDirectory(path:String) {
if (!FileSystem.exists(path)) {
try {
FileSystem.createDirectory(path);
} catch (e) {
Context.warning('Failed to create directory: ${path}. Error: ${e}', Context.currentPos());
}
}
}
private static function findAssetsMeta(metaAccess:Array<MetadataEntry>):MetadataEntry {
if (metaAccess == null) return null;
for (meta in metaAccess) {
if (meta.name == ":Assets" || meta.name == "Assets") {
return meta;
}
}
return null;
}
private static function extractString(expr:Expr):String {
switch (expr.expr) {
case EConst(CString(s)): return s;
default: Context.error("Expected string literal", expr.pos);
}
return null;
}
private static function getAssetType(path:String):String {
var ext = path.split(".").pop().toLowerCase();
return (switch (ext) {
case "jpg", "jpeg", "png", "gif", "bmp": "image";
case "mp3", "ogg", "wav": "sound";
case "ttf", "otf": "font";
case "json", "xml", "txt": "text";
default: "binary";
}).toUpperCase();
}
}

View File

@@ -1,15 +1,9 @@
#!/bin/bash #!/bin/bash
set -e
# Compile the project using the Haxe compiler with the specified build.hxml configuration # Compile the project using the Haxe compiler with the specified build.hxml configuration
haxe build.hxml haxe build.hxml
# Check if the compilation was successful
if [ $? -eq 0 ]; then
echo "Compilation successful!"
else
echo "Compilation failed!"
fi
# Run the compiled project # Run the compiled project
# We need to run ApplicationMain-debug within the bin/cpp directory as the current working directory # Run beside the staged manifest, assets and native libraries.
(cd ./bin/cpp; ./ApplicationMain-debug) (cd ./bin/cpp; ./ApplicationMain)

View File

@@ -1,16 +1,11 @@
package; package;
import openfl.display.Sprite; import openfl.display.Sprite;
import openfl.Lib;
import sys.FileSystem;
import Assets;
class Main extends Sprite { class Main extends Sprite {
public function new() { public function new() {
super(); super();
Lib.current.addChild(this);
// Draw background to verify app is running // Draw background to verify app is running
graphics.beginFill(0x3498db, 1); graphics.beginFill(0x3498db, 1);
graphics.drawRect(0, 0, 800, 600); graphics.drawRect(0, 0, 800, 600);

View File

@@ -1,10 +1,8 @@
package; package;
#if sys
import haxe.io.Path; import haxe.io.Path;
import openfl.utils.AssetLibrary; import lime.utils.AssetLibrary;
import openfl.utils.AssetManifest; import lime.utils.Assets;
import openfl.utils.Assets as LimeAssets;
import sys.FileSystem; import sys.FileSystem;
@:access(lime.utils.Assets) @:access(lime.utils.Assets)
@@ -14,44 +12,32 @@ import sys.FileSystem;
public static var rootPath:String; public static var rootPath:String;
public static function init(config:Dynamic):Void { public static function init(config:Dynamic):Void {
rootPath = ""; preloadLibraries = [];
preloadLibraryNames = [];
rootPath = null;
if (config != null && Reflect.hasField(config, "rootPath")) {
rootPath = Reflect.field(config, "rootPath");
if (!StringTools.endsWith(rootPath, "/")) rootPath += "/";
}
if (rootPath == null) rootPath = lime.system.System.applicationDirectory;
var manifestDirPath = Path.join([rootPath, "manifest"]);
if (!FileSystem.exists(manifestDirPath) || !FileSystem.isDirectory(manifestDirPath)) {
throw 'Asset manifest directory not found: $manifestDirPath';
}
var manifestDirPath = Path.join(["manifest"]);
var manifests = FileSystem.readDirectory(manifestDirPath); var manifests = FileSystem.readDirectory(manifestDirPath);
manifests.sort(Reflect.compare);
for (manifest in manifests) { for (manifest in manifests) {
trace(manifest);
if (StringTools.endsWith(manifest, ".json")) { if (StringTools.endsWith(manifest, ".json")) {
var manifestPath = Path.join([manifestDirPath, manifest]); var manifestName = Path.withoutExtension(manifest);
loadManifest(manifestPath, manifest.split(".json")[0]); Assets.libraryPaths[manifestName] = Path.join([manifestDirPath, manifest]);
}
else{
throw("we eating shit tonight");
var library = Assets.getLibrary(manifestName);
if (library != null) preloadLibraries.push(library);
else preloadLibraryNames.push(manifestName);
} }
} }
} }
public static function loadManifest(manifestPath, manifestName) {
if (!FileSystem.exists(manifestPath)) {
trace("Manifest file not found: " + manifestPath);
return;
} }
try {
var manifest = AssetManifest.fromFile(manifestPath);
if (manifest != null) {
var library = AssetLibrary.fromManifest(manifest);
if (library != null) {
trace("Library: `"+library+"`");
trace("manifestName: `"+manifestName+"`");
preloadLibraries.push(library);
preloadLibraryNames.push(manifestName);
// LimeAssets.registerLibrary(manifestName, library);
openfl.utils.Assets.registerLibrary(manifestName, library);
}
}
} catch (e:Dynamic) {
trace("Error loading asset manifest: " + e);
}
}
}
#end

View File

@@ -25,6 +25,9 @@ class AssetMacro {
Context.warning("AssetMacro is running", Context.currentPos()); Context.warning("AssetMacro is running", Context.currentPos());
#end #end
// A compilation server can retain macro statics between builds.
assetMap = new Map();
// Get current build context // Get current build context
var fields = Context.getBuildFields(); var fields = Context.getBuildFields();
var localClass = Context.getLocalClass().get(); var localClass = Context.getLocalClass().get();
@@ -41,6 +44,7 @@ class AssetMacro {
// Create directories if they don't exist // Create directories if they don't exist
createDirectory(manifestDir); createDirectory(manifestDir);
createDirectory(assetsOutputDir); createDirectory(assetsOutputDir);
copyLimeNativeLibrary(outputDir);
// Process asset fields // Process asset fields
for (field in fields) { for (field in fields) {
@@ -237,6 +241,48 @@ class AssetMacro {
} }
} }
/** Stage the Lime CFFI module that matches the selected haxelib version. */
private static function copyLimeNativeLibrary(outputDir:String):Void {
var platformDirectory = if (Context.defined("linux")) {
"Linux64";
} else if (Context.defined("windows")) {
"Windows64";
} else if (Context.defined("mac")) {
Context.defined("arm64") ? "MacArm64" : "Mac64";
} else {
return;
}
var limeRoot:String = null;
for (classPath in Context.getClassPath()) {
var normalized = classPath.split("\\").join("/");
if (normalized.indexOf("/lime/") != -1 && normalized.endsWith("/src/")) {
limeRoot = normalized.substr(0, normalized.length - 4);
break;
}
}
if (limeRoot == null) {
Context.warning("Could not locate Lime native library from the compiler class path", Context.currentPos());
return;
}
var source = limeRoot + "ndll/" + platformDirectory + "/lime.ndll";
if (!FileSystem.exists(source) && platformDirectory.endsWith("64")) {
platformDirectory = platformDirectory.substr(0, platformDirectory.length - 2);
source = limeRoot + "ndll/" + platformDirectory + "/lime.ndll";
}
if (!FileSystem.exists(source)) {
Context.warning("Lime native library not found: " + source, Context.currentPos());
return;
}
try {
File.copy(source, outputDir + "/lime.ndll");
} catch (e:Dynamic) {
Context.error("Failed to copy Lime native library: " + e, Context.currentPos());
}
}
private static function findAssetsMeta(metaAccess:Array<MetadataEntry>):MetadataEntry { private static function findAssetsMeta(metaAccess:Array<MetadataEntry>):MetadataEntry {
if (metaAccess == null) return null; if (metaAccess == null) return null;