diff --git a/.github/workflows/hbuild.yml b/.github/workflows/hbuild.yml index 995cfc1ea..3d186ae43 100644 --- a/.github/workflows/hbuild.yml +++ b/.github/workflows/hbuild.yml @@ -43,7 +43,7 @@ jobs: path: tools/hbuild/linux64-hbuild build-macos: - runs-on: macos-12 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - uses: dlang-community/setup-dlang@4c99aa991ce7d19dd3064de0a4f2f6b2f152e2d7 diff --git a/README.md b/README.md index 913afb720..0f1bc05e9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ Getting Started +[![HBuild](https://github.com/MrcSnm/HipremeEngine/actions/workflows/hbuild.yml/badge.svg)](https://github.com/MrcSnm/HipremeEngine/actions/workflows/hbuild.yml) + + The engine maintains some global options which you can modify it on source/global/opts.d: diff --git a/source/hip/graphics/g2d/profiling.d b/source/hip/graphics/g2d/profiling.d index 9b2834a27..e0123a382 100644 --- a/source/hip/graphics/g2d/profiling.d +++ b/source/hip/graphics/g2d/profiling.d @@ -10,72 +10,76 @@ import hip.graphics.g2d.renderer2d; */ void drawGCStats(int x = 0, int y = -1) { - import core.memory; - import hip.util.string; - import hip.util.data_structures; - import hip.math.utils; - GC.Stats stats = GC.stats; - GC.ProfileStats prof = GC.profileStats; - - static struct ByteUnit + import hip.config.opts; + static if(!CustomRuntime) { - double data; - string unit; + import core.memory; + import hip.util.string; + import hip.util.data_structures; + import hip.math.utils; + GC.Stats stats = GC.stats; + GC.ProfileStats prof = GC.profileStats; - String asString() @nogc + static struct ByteUnit { - return String(String(data).toString.limitDecimalPlaces(2), unit); + double data; + string unit; + + String asString() @nogc + { + return String(String(data).toString.limitDecimalPlaces(2), unit); + } } - } - ByteUnit formatFromBytes(size_t byteCount) @nogc - { - double actualResult = byteCount; + ByteUnit formatFromBytes(size_t byteCount) @nogc + { + double actualResult = byteCount; - if(actualResult <= 1000) - return ByteUnit(floorDecimal(actualResult, 2), " B"); - actualResult/= 1000; - if(actualResult <= 1000) - return ByteUnit(floorDecimal(actualResult, 2), " KB"); - actualResult/= 1000; - return ByteUnit(floorDecimal(actualResult, 2), " MB"); - actualResult/= 1000; - return ByteUnit(floorDecimal(actualResult, 2), " GB"); + if(actualResult <= 1000) + return ByteUnit(floorDecimal(actualResult, 2), " B"); + actualResult/= 1000; + if(actualResult <= 1000) + return ByteUnit(floorDecimal(actualResult, 2), " KB"); + actualResult/= 1000; + return ByteUnit(floorDecimal(actualResult, 2), " MB"); + actualResult/= 1000; + return ByteUnit(floorDecimal(actualResult, 2), " GB"); - } - String timeOnPause; - String timeOnCollection; + } + String timeOnPause; + String timeOnCollection; - prof.totalPauseTime.toString((string data) - { - timeOnPause~= data; - }); - prof.totalCollectionTime.toString((string data) - { - timeOnCollection~= data; - }); + prof.totalPauseTime.toString((string data) + { + timeOnPause~= data; + }); + prof.totalCollectionTime.toString((string data) + { + timeOnCollection~= data; + }); - scope auto toPrint = [ - String("GC Stats: "), - String("\tMemory Used: ", formatFromBytes(stats.usedSize).asString), - String("\tFree Memory: ", formatFromBytes(stats.freeSize).asString), - String("\tTime Paused on GC: ", timeOnPause), - String("\tTime Spent on Collection:", timeOnCollection), - String("\tCollections Count: ", prof.numCollections), - ].staticArray; + scope auto toPrint = [ + String("GC Stats: "), + String("\tMemory Used: ", formatFromBytes(stats.usedSize).asString), + String("\tFree Memory: ", formatFromBytes(stats.freeSize).asString), + String("\tTime Paused on GC: ", timeOnPause), + String("\tTime Spent on Collection:", timeOnCollection), + String("\tCollections Count: ", prof.numCollections), + ].staticArray; - int lbSize = 40; - int totalSize = lbSize*cast(int)toPrint.length; - if(x < 0) - x = 0; - if(y < 0) - { - import hip.hiprenderer.renderer; - Viewport vp = HipRenderer.getCurrentViewport; - y = vp.worldHeight - totalSize; - } + int lbSize = 40; + int totalSize = lbSize*cast(int)toPrint.length; + if(x < 0) + x = 0; + if(y < 0) + { + import hip.hiprenderer.renderer; + Viewport vp = HipRenderer.getCurrentViewport; + y = vp.worldHeight - totalSize; + } - foreach(i, ref String str; toPrint) - drawText(str.toString, x, y+lbSize*cast(int)i); + foreach(i, ref String str; toPrint) + drawText(str.toString, x, y+lbSize*cast(int)i); + } } \ No newline at end of file diff --git a/tools/hbuild/dub.json b/tools/hbuild/dub.json index 6444370e4..7e8067761 100644 --- a/tools/hbuild/dub.json +++ b/tools/hbuild/dub.json @@ -5,8 +5,8 @@ "copyright": "Copyright © 2023, Hipreme", "dependencies": { "arsd-official:terminal": "~>11.5.0", - "arsd-official:cgi": "~>11.5.0", - "redub": "~>1.18.6", + "redub": "~>1.20.0", + "handy-httpd": "~>8.4.3", "archive": "~>0.7.1", "wasm-sourcemaps": {"repository": "git+https://github.com/MrcSnm/wasm-sourcemaps", "version": "69e6c80"} }, diff --git a/tools/hbuild/source/commons.d b/tools/hbuild/source/commons.d index e6221c717..3eeaf3812 100644 --- a/tools/hbuild/source/commons.d +++ b/tools/hbuild/source/commons.d @@ -222,14 +222,12 @@ size_t selectChoiceBase(ref Terminal terminal, ref RealTimeConsoleInput input, C string selectionTitle, size_t selectedChoice = 0) { bool exit; + enum ESC = 983067; enum ArrowUp = 983078; enum ArrowDown = 983080; enum SelectionHint = "Select an option by using W/S or Arrow Up/Down and choose it by pressing Enter."; - terminal.clear(); - terminal.color(Color.DEFAULT, Color.DEFAULT); - terminal.writelnHighlighted(selectionTitle); - terminal.writeln(SelectionHint); + static bool isFirst = true; static void changeChoice(ref Terminal t, Choice[] choices, string title, Choice current, Choice next, int nextCursorOffset) { int currCursor = t.cursorY; @@ -244,10 +242,11 @@ size_t selectChoiceBase(ref Terminal terminal, ref RealTimeConsoleInput input, C t.flush; } - static void changeChoiceClear(ref Terminal t, Choice[] choices, string title, Choice current, Choice next, int nextCursorOffset) + static void changeChoiceClear(ref Terminal t, Choice[] choices, string title, Choice current, Choice next, int nextCursorOffset, bool bClear) { t.color(Color.DEFAULT, Color.DEFAULT); - t.clear(); + if(bClear) + t.clear(); t.writelnHighlighted(title); t.writeln(SelectionHint); foreach(i, c; choices) @@ -259,11 +258,9 @@ size_t selectChoiceBase(ref Terminal terminal, ref RealTimeConsoleInput input, C t.flush; } + if(!isFirst) + terminal.clear(); int startLine = terminal.cursorY; - terminal.color(Color.DEFAULT, Color.DEFAULT); - - foreach(i, choice; choices) - terminal.write(choice.name, i == choices.length - 1 ? "" : "\n"); terminal.flush(); terminal.moveTo(0, startLine + cast(int)selectedChoice); @@ -273,9 +270,12 @@ size_t selectChoiceBase(ref Terminal terminal, ref RealTimeConsoleInput input, C size_t oldChoice = selectedChoice; while(!exit) { - changeChoiceClear(terminal, choices, selectionTitle, choices[oldChoice], choices[selectedChoice], cast(int)(cast(long)selectedChoice-oldChoice)); + changeChoiceClear(terminal, choices, selectionTitle, choices[oldChoice], choices[selectedChoice], cast(int)(cast(long)selectedChoice-oldChoice), !isFirst); + isFirst = false; oldChoice = selectedChoice; - CheckInput: switch(input.getch) + + size_t choice = input.getch; + CheckInput: switch(choice) { case 'w', 'W', ArrowUp: selectedChoice = (selectedChoice + choices.length - 1) % choices.length; @@ -283,6 +283,10 @@ size_t selectChoiceBase(ref Terminal terminal, ref RealTimeConsoleInput input, C case 's', 'S', ArrowDown: selectedChoice = (selectedChoice+1) % choices.length; break; + case ESC: + selectedChoice = choices.length - 1; + exit = true; + break; case '\n': exit = true; break; @@ -290,9 +294,10 @@ size_t selectChoiceBase(ref Terminal terminal, ref RealTimeConsoleInput input, C } } terminal.moveTo(0, cast(int)startLine); - foreach(i; 0..choices.length) + //Title + SelectionHint + foreach(i; 0..choices.length+3) terminal.moveTo(0, cast(int)(startLine+i)), terminal.clearToEndOfLine(); - terminal.moveTo(0, cast(int)startLine); + terminal.moveTo(0, cast(int)startLine+1); //Jump title terminal.writelnSuccess(">> ", choices[selectedChoice].name); terminal.showCursor(); @@ -986,6 +991,8 @@ struct DubArguments int waitRedub(ref Terminal t, DubArguments dArgs, out ProjectDetails proj, string copyLinkerFilesTo = null) { import redub.logging; + import redub.buildapi; + import redub.api; if(execDubBase(t, dArgs) == -1) return -1; setLogLevel(dArgs._opts.dubVerbose ? LogLevel.verbose : LogLevel.info); @@ -995,9 +1002,12 @@ int waitRedub(ref Terminal t, DubArguments dArgs, out ProjectDetails proj, strin CompilationDetails(dArgs.getCompiler(), dArgs._arch), ProjectToParse(dArgs._configuration, std.file.getcwd(), null, dArgs._recipe), InitialDubVariables.init, - BuildType.debug_ + BuildType.profile_gc ); - if(buildProject(proj).error) return 1; + try { + if(buildProject(proj).error) return 1; + } + catch(BuildException err) { return 1; } if(copyLinkerFilesTo.length) { import tools.copylinkerfiles; diff --git a/tools/hbuild/source/features/dmd.d b/tools/hbuild/source/features/dmd.d index 1f47c9764..16f990a72 100644 --- a/tools/hbuild/source/features/dmd.d +++ b/tools/hbuild/source/features/dmd.d @@ -48,7 +48,10 @@ void initialize() ), outputPath: "$TEMP/$NAME", )], toDelegate(&installDmd), ["$CWD/D"]), - (ref Terminal, string installPath){addToPath(installPath.buildNormalizedPath);}, + (ref Terminal t, string installPath) + { + addToPath(installPath.dirName); + }, VersionRange.parse("2.105.0", "2.106.0") ); } diff --git a/tools/hbuild/source/global_opts.d b/tools/hbuild/source/global_opts.d index af08b6cce..a23c98940 100644 --- a/tools/hbuild/source/global_opts.d +++ b/tools/hbuild/source/global_opts.d @@ -5,35 +5,70 @@ bool serverStarted; bool appleClean; -import std.concurrency; +import core.thread; import core.sync.semaphore; import commons; -private Tid serverTid; +private Thread serverThread; shared ushort gameServerPort; void startServer(shared ushort* usingPort) { if(serverStarted) return; - import serve; - static void startTheServer(shared ushort* usingPort, shared Semaphore sem) - { - hipengineCgiMain!(serveGameFiles)([], getHipPath("build", "wasm"), usingPort, sem); - } + import server; + import core.stdc.stdlib; serverStarted = true; - Semaphore s = new Semaphore(); - serverTid = spawn(&startTheServer, usingPort, cast(shared)s); - s.wait; + serverThread = new Thread(() + { + hipengineStartServer([], usingPort, getHipPath("build", "wasm"), cast(shared)s); + }).start(); + + + version(Windows) + { + import core.sys.windows.windef; // Windows API bindings + import core.sys.windows.wincon; // Windows API bindings + + static extern(Windows) BOOL handleCtrlC(DWORD ctrlType) nothrow + { + if (ctrlType == CTRL_C_EVENT) // CTRL+C signal + { + try exitServer(); + catch(Exception e){} + exit(0); + } + return FALSE; + } + if (!SetConsoleCtrlHandler(&handleCtrlC, TRUE)) + { + throw new Exception("Failed to register CTRL+C handler."); + } + } + else version(Posix) + { + import core.sys.posix.signal; + static extern(C) void handleCtrlC(int signum) + { + exitServer(); + exit(0); + } + alias fn = extern(C) void function(int) nothrow @nogc; + signal(SIGINT, cast(fn)&handleCtrlC); + } + + s.wait; } void exitServer() { if(!serverStarted) return; - import serve; + import core.stdc.stdlib; + import server; serverStarted = false; - serverTid = Tid.init; - stopServer(); + serverThread = null; + stopServer(); + exit(0); } \ No newline at end of file diff --git a/tools/hbuild/source/serve.d b/tools/hbuild/source/serve.d deleted file mode 100644 index 6c9e8cbbc..000000000 --- a/tools/hbuild/source/serve.d +++ /dev/null @@ -1,199 +0,0 @@ -module serve; - -import arsd.cgi; -import std.stdio; -import std.file; -import std.string; -import std.process; - -// -Wl,--export=__heap_base - -// https://github.com/skoppe/wasm-sourcemaps - -enum DEFAULT_PATH="build"; -private __gshared string startPath; - -string extendedContentTypeFromFileExtension(string thing) -{ - string ret = contentTypeFromFileExtension(thing); - if(ret != null) - return ret; - - if(ret.endsWith(".ogg")) - return "application/ogg"; - if(ret.endsWith(".mp4")) - return "application/mp4"; - - return "application/octet-stream"; -} - -import core.thread; -import core.sync.mutex; -import core.sync.semaphore; -void pushWebsocketMessage(string message) -{ - synchronized - { - foreach(ref conn; connections) - conn.messages~= message; - } - // socket.send(message); - // synchronized websocketMessages~= message; -} - -struct Connection -{ - size_t id; - string[] messages; -} -private __gshared size_t id; -private __gshared Connection*[] connections; - -void serveGameFiles(Cgi cgi) -{ - import std.path; - string targetPath = buildNormalizedPath(startPath, DEFAULT_PATH); - string contentType = extendedContentTypeFromFileExtension(cgi.pathInfo); - string file = buildNormalizedPath(targetPath, cgi.pathInfo[1..$]); - - if(cgi.websocketRequested()) - { - auto socket = cgi.acceptWebsocket(); - Connection conn = Connection(id); - synchronized - { - connections~= &conn; - id++; - } - enum __updateMsg = 0xff; - auto msg = socket.recv(); - while(msg.opcode != WebSocketOpcode.close) - { - synchronized - { - foreach(socketMsg; conn.messages) - socket.send(socketMsg); - conn.messages.length = 0; - } - msg = socket.recv(); - } - - synchronized - { - socket.close(); - import std.algorithm; - ptrdiff_t index = countUntil!((Connection* c) => c.id == conn.id)(connections); - if(index != -1) - { - connections = connections[0..index]~ connections[index+1..$]; - } - } - writeln("AutoReloading WebSocket[", conn.id, "] closed."); - } - else if(cgi.pathInfo == "/") - { - string indexHTML = buildNormalizedPath(targetPath, "index.html"); - if(exists(indexHTML)) - { - import std.string; - string reloadServer = replace(import("reload_server.js"), "$WEBSOCKET_SERVER$", "ws://localhost:"~server.listeningPort.to!string); - cgi.write(readText(indexHTML)~"", true); - } - else - { - // index - string html = "