More public 0.6.4 fixes (#1016)

Co-authored-by: Hyper_ <40342021+NotHyper-474@users.noreply.github.com>
Co-authored-by: Lasercar <64717068+Lasercar@users.noreply.github.com>
Co-authored-by: Lasercar <B_l_u_t_a_c_k@hotmail.com.au>
Co-authored-by: anysad <anysadiscool@gmail.com>
Co-authored-by: Hundrec <hundrecard@gmail.com>
Co-authored-by: VioletSnowLeopard <ngzeretzke@gmail.com>
Co-authored-by: unknown <47027981+ACrazyTown@users.noreply.github.com>
Co-authored-by: kade-github <26305836+Kade-github@users.noreply.github.com>
Co-authored-by: Abnormal <86753001+AbnormalPoof@users.noreply.github.com>
Co-authored-by: CrusherNotDrip <90648119+CrusherNotDrip@users.noreply.github.com>
Co-authored-by: cyn0x8 <cyn0x8+git@gmail.com>
This commit is contained in:
Eric
2025-05-08 10:28:39 -07:00
committed by GitHub
co-authored by Hyper_ Lasercar Lasercar anysad Hundrec VioletSnowLeopard unknown kade-github Abnormal CrusherNotDrip cyn0x8
parent 35f7113091
commit 44c32cb0fc
17 changed files with 225 additions and 139 deletions
+5 -10
View File
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adaptive turns VSync off during FPS drops, which is better if supported by your graphics card.
- Doesn't work on Mac, sorry!
- Otis now has muzzle flashes when shooting.
- Added a little easter egg to one of Pico's Results Screen animations.
- Overhauled the sticker system to allow modders to easily add custom sticker sets. ([cdc468b](https://github.com/FunkinCrew/Funkin/commit/cdc468ba15683b3c0f54015e337673c77ddd7962)) - by @AbnormalPoof in [#4003](https://github.com/FunkinCrew/Funkin/pull/4003)
- Implemented a macro to optimize registries and entries. ([1497521](https://github.com/FunkinCrew/Funkin/commit/14975212a918adb1a5d96a349ec9e8e63c9fc64b)) - by @lemz1 in [#3694](https://github.com/FunkinCrew/Funkin/pull/3694)
- The macro is now used to retrieve base game assets. ([963e2ec](https://github.com/FunkinCrew/Funkin/commit/963e2ecd630fae24dae4206d315618cf4c4be6db)) - by @lemz1 in [#4707](https://github.com/FunkinCrew/Funkin/pull/4707)
@@ -43,6 +44,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Renamed “Auto Pause” preference to “Pause on Unfocus” for clarity. ([52be941](https://github.com/FunkinCrew/Funkin/commit/52be941b4503da0ac76918e2482ab1804866f2cf)) - by @JackXson-Real in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
- Overhauled `FileUtil`, introducing various fixes, new functions, and sandboxing. ([95ade2a](https://github.com/FunkinCrew/Funkin/commit/95ade2a08b7709e8208ec1b3e123bf5b4308ba10)) - by @cyn0x8 in [#3032](https://github.com/FunkinCrew/Funkin/pull/3032)
### Fixed
- Story mode medals are now awarded even without scoring a new personal best.
- Highscores are now submitted to Newgrounds even without scoring a new personal best.
- The Freeplay menu no longer displays songs without Erect variations when returning from an Erect variation song.
- Fixed Freeplay DJ animations for Boyfriend and Pico when idling (properly this time).
- Alternate instrumentals for Cocoa, Senpai, Roses, and Stress are now locked until their Pico Mix is beaten.
@@ -183,16 +188,6 @@ The Pit Stop 2 update!
- Removed an unused class from Polymod blacklist. ([06c12e3](https://github.com/FunkinCrew/Funkin/commit/06c12e36c6bd6df4e2be32a3bec540172e79e162)) - by @AbnormalPoof in [#3729](https://github.com/FunkinCrew/Funkin/pull/3729)
- Many additional small bug fixes.
### Fixed
* @PatoFlamejanteTV made their first contribution in [#3843](https://github.com/FunkinCrew/Funkin/pull/3843)
* @sphis-Sinco made their first contribution in [#3881](https://github.com/FunkinCrew/Funkin/pull/3881)
* @MrMadera made their first contribution in [#3934](https://github.com/FunkinCrew/Funkin/pull/3934)
* @MidyGamy made their first contribution in [#4068](https://github.com/FunkinCrew/Funkin/pull/4068)
* @Lasercar made their first contribution in [#4082](https://github.com/FunkinCrew/Funkin/pull/4082)
* @MrScottyPieey made their first contribution in [#4085](https://github.com/FunkinCrew/Funkin/pull/4085)
## New Contributors for 0.6.0
* @PatoFlamejanteTV made their first contribution in [#3843](https://github.com/FunkinCrew/Funkin/pull/3843)
+2 -1
View File
@@ -44,7 +44,8 @@ class Medals
if (NewgroundsClient.instance.isLoggedIn())
{
var medalList = NewgroundsClient.instance.medals;
if (medalList == null) return;
@:privateAccess
if (medalList == null || medalList._map == null) return;
var medalData:Null<MedalData> = medalList.get(medal.getId());
@:privateAccess
@@ -66,6 +66,8 @@ class FlxAtlasSprite extends FlxAnimate
throw 'FlxAtlasSprite not initialized properly. Are you sure the path (${path}) exists?';
}
onAnimationComplete.add(cleanupAnimation);
// This defaults the sprite to play the first animation in the atlas,
// then pauses it. This ensures symbols are intialized properly.
this.anim.play('');
@@ -317,7 +319,6 @@ class FlxAtlasSprite extends FlxAnimate
else if (fr != null && anim.curFrame != anim.length - 1)
{
anim.curFrame--;
cleanupAnimation(currentAnimation ?? "");
_onAnimationComplete();
}
}
+6 -2
View File
@@ -80,8 +80,10 @@ class PolymodHandler
*/
public static function loadAllMods():Void
{
#if sys
// Create the mod root if it doesn't exist.
createModRoot();
#end
trace('Initializing Polymod (using all mods)...');
loadModsById(getAllModIds());
}
@@ -91,9 +93,10 @@ class PolymodHandler
*/
public static function loadEnabledMods():Void
{
#if sys
// Create the mod root if it doesn't exist.
createModRoot();
#end
trace('Initializing Polymod (using configured mods)...');
loadModsById(Save.instance.enabledModIds);
}
@@ -103,9 +106,10 @@ class PolymodHandler
*/
public static function loadNoMods():Void
{
#if sys
// Create the mod root if it doesn't exist.
createModRoot();
#end
// We still need to configure the debug print calls etc.
trace('Initializing Polymod (using no mods)...');
loadModsById([]);
+6 -5
View File
@@ -1229,7 +1229,7 @@ class PlayState extends MusicBeatSubState
if (!isMinimalMode)
{
if (iconP1 != null) iconP1.updatePosition();
if (iconP1 != null) iconP2.updatePosition();
if (iconP2 != null) iconP2.updatePosition();
}
// Transition to the game over substate.
@@ -1898,14 +1898,15 @@ class PlayState extends MusicBeatSubState
// Position the player strumline on the right half of the screen
playerStrumline.x = (FlxG.width / 2 + Constants.STRUMLINE_X_OFFSET) + (cutoutSize / 2.0); // Classic style
// playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET; // Centered style
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET - noteStyle.getStrumlineOffsets()[1] : Constants.STRUMLINE_Y_OFFSET;
playerStrumline.zIndex = 1001;
playerStrumline.cameras = [camHUD];
// Position the opponent strumline on the left half of the screen
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET + cutoutSize;
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET;
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET - noteStyle.getStrumlineOffsets()[1] : Constants.STRUMLINE_Y_OFFSET;
opponentStrumline.zIndex = 1000;
opponentStrumline.cameras = [camHUD];
@@ -2889,7 +2890,7 @@ class PlayState extends MusicBeatSubState
#end
// 9: Toggle the old icon.
if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon();
if (FlxG.keys.justPressed.NINE && iconP1 != null) iconP1.toggleOldIcon();
#if FEATURE_DEBUG_FUNCTIONS
// PAGEUP: Skip forward two sections.
@@ -572,7 +572,7 @@ class DebugBoundingState extends FlxState
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(saveString,);
_file.save(saveString, fileName);
}
}
@@ -3225,7 +3225,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
#if sys
// TODO: Is there a way to open a folder and highlight a file in it?
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
WindowUtil.openFolder(absoluteBackupsPath);
FileUtil.openFolder(absoluteBackupsPath);
return true;
#else
trace('No file system access, cannot open backups folder.');
@@ -5838,6 +5838,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{
uiCamera.revive();
FlxG.cameras.reset(uiCamera);
uiCamera.onResize();
add(this.root);
}
@@ -91,6 +91,8 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
#end
linkRecentChart.onClick = function(_event) {
linkRecentChart.hide();
this.hideDialog(DialogButton.CANCEL);
// Load chart from file
@@ -1267,7 +1267,7 @@ class StageEditorState extends UIState
case "open folder":
#if sys
var absoluteBackupsPath:String = haxe.io.Path.join([Sys.getCwd(), BACKUPS_PATH]);
WindowUtil.openFolder(absoluteBackupsPath);
FileUtil.openFolder(absoluteBackupsPath);
#end
case "test stage":
@@ -5,7 +5,6 @@ import haxe.ui.containers.dialogs.Dialog.DialogButton;
import funkin.util.FileUtil;
import haxe.io.Path;
import funkin.util.DateUtil;
import funkin.util.WindowUtil;
using StringTools;
@@ -57,7 +56,7 @@ class BackupAvailableDialog extends Dialog
// :[
#if sys
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), StageEditorState.BACKUPS_PATH]);
WindowUtil.openFolder(absoluteBackupsPath);
FileUtil.openFolder(absoluteBackupsPath);
#end
}
+1 -1
View File
@@ -35,7 +35,7 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
loopNum--;
}
while (loopNum > 0)
while (loopNum >= 0)
{
group.members[loopNum].digit = 0;
loopNum--;
+6 -5
View File
@@ -567,13 +567,13 @@ class FreeplayState extends MusicBeatSubState
switch (str)
{
case 'fav':
generateSongList({filterType: FAVORITE}, true);
generateSongList({filterType: FAVORITE}, true, false);
case 'ALL':
generateSongList(null, true);
generateSongList(null, true, false);
case '#':
generateSongList({filterType: REGEXP, filterData: '0-9'}, true);
generateSongList({filterType: REGEXP, filterData: '0-9'}, true, false);
default:
generateSongList({filterType: REGEXP, filterData: str}, true);
generateSongList({filterType: REGEXP, filterData: str}, true, false);
}
// If the current song is still in the list, or if it was random, we'll land on it
@@ -2033,7 +2033,8 @@ class FreeplayState extends MusicBeatSubState
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.data.id, currentDifficulty, currentVariation);
intendedScore = songScore?.score ?? 0;
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
intendedCompletion = songScore == null ? 0.0 : Math.max(0, ((songScore.tallies.sick +
songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes));
rememberedDifficulty = currentDifficulty;
grpCapsules.members[curSelected].refreshDisplay((prepForNewRank == true) ? false : true);
}
@@ -108,6 +108,7 @@ class MainMenuState extends MusicBeatState
createMenuItem('freeplay', 'mainmenu/freeplay', function() {
persistentDraw = true;
persistentUpdate = false;
rememberedSelectedIndex = menuItems.selectedIndex;
// Freeplay has its own custom transition
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
@@ -422,6 +423,7 @@ class MainMenuState extends MusicBeatState
{
if (menuItems.enabled && !menuItems.busy)
{
rememberedSelectedIndex = menuItems.selectedIndex;
FlxG.switchState(() -> new TitleState());
FunkinSound.playOnce(Paths.sound('cancelMenu'));
}
+138 -57
View File
@@ -52,24 +52,44 @@ class FileUtil
/**
* Paths which should not be deleted or modified by scripts.
*/
private static final PROTECTED_PATHS:Array<String> = [
'',
'assets',
'manifest',
'manifest/*',
'plugins',
'plugins/*',
'Funkin.exe',
'Funkin',
'libvlc.dll',
'libvlccore.dll',
'lime.ndll'
];
public static var PROTECTED_PATHS(get, never):Array<String>;
public static function get_PROTECTED_PATHS():Array<String>
{
final protected:Array<String> = [
'',
'.',
'assets',
'assets/*',
'backups',
'backups/*',
'manifest',
'manifest/*',
'plugins',
'plugins/*',
'Funkin.exe',
'Funkin',
'icon.ico',
'libvlc.dll',
'libvlccore.dll',
'lime.ndll',
'scores.json'
];
#if sys
for (i in 0...protected.length)
{
protected[i] = sys.FileSystem.fullPath(Path.join([gameDirectory, protected[i]]));
}
#end
return protected;
}
/**
* Invalid filesystem characters.
* Regex for invalid filesystem characters.
*/
private static final INVALID_CHARS:Array<String> = [':', '*', '?', '"', '<', '>', '|'];
public static final INVALID_CHARS:EReg = ~/[:*?"<>|\n\r\t]/g;
#if sys
private static var _gameDirectory:Null<String> = null;
@@ -82,7 +102,7 @@ class FileUtil
return _gameDirectory;
}
return _gameDirectory = Path.directory(Path.normalize(sys.FileSystem.fullPath(Sys.programPath())));
return _gameDirectory = sys.FileSystem.fullPath(Path.directory(Sys.programPath()));
}
#end
@@ -601,8 +621,6 @@ class FileUtil
throw 'Target path is a directory, not a file: "$path"';
}
createDirIfNotExists(Path.directory(path));
var shouldWrite:Bool = true;
switch (mode)
{
@@ -791,14 +809,14 @@ class FileUtil
*/
public static function createDirIfNotExists(dir:String):Void
{
#if sys
if (!directoryExists(dir))
{
#if sys
sys.FileSystem.createDirectory(dir);
#else
throw 'Directory creation is not supported on this platform.';
#end
}
#else
throw 'Directory creation is not supported on this platform.';
#end
}
/**
@@ -1124,8 +1142,64 @@ class FileUtil
};
}
public static function openFolder(pathFolder:String) // what is this for??
WindowUtil.openFolder(pathFolder);
/**
* Runs platform-specific code to open a path in the file explorer.
*
* @param pathFolder The path of the folder to open.
*/
public static function openFolder(pathFolder:String):Void
{
#if sys
pathFolder = pathFolder.trim();
if (!directoryExists(pathFolder))
{
throw 'Path is not a directory: "$pathFolder"';
}
#if windows
Sys.command('explorer', [pathFolder.replace('/', '\\')]);
#elseif mac
// mac could be fuckie with where the log folder is relative to the game file...
// if this comment is still here... it means it has NOT been verified on mac yet!
//
// FileUtil.hx note: this was originally used to open the logs specifically!
// thats why the above comment is there!
Sys.command('open', [pathFolder]);
#elseif linux
// TODO: implement linux
// some shit with xdg-open :thinking: emoji...
#end
#else
throw 'External folder open is not supported on this platform.';
#end
}
/**
* Runs platform-specific code to open a file explorer and select a specific file.
*
* @param path The path of the file to select.
*/
public static function openSelectFile(path:String):Void
{
#if sys
path = path.trim();
if (!pathExists(path))
{
throw 'Path does not exist: "$path"';
}
#if windows
Sys.command('explorer', ['/select,', path.replace('/', '\\')]);
#elseif mac
Sys.command('open', ['-R', path]);
#elseif linux
// TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file.
Sys.command('open', [path]);
#end
#else
throw 'External file selection is not supported on this platform.';
#end
}
private static function convertTypeFilter(?typeFilter:Array<FileFilter>):Null<String>
{
@@ -1160,7 +1234,8 @@ class FileUtilSandboxed
*/
public static function sanitizePath(path:String):String
{
if (path == null)
path = (path ?? '').trim();
if (path == '')
{
#if sys
return FileUtil.gameDirectory;
@@ -1174,19 +1249,14 @@ class FileUtilSandboxed
path = path.substring(path.lastIndexOf(':') + 1);
}
path = path.trim().replace('\\', '/');
path = path.replace('\\', '/');
while (path.contains('//'))
{
path = path.replace('//', '/');
}
@:privateAccess for (char in FileUtil.INVALID_CHARS)
{
path = path.replace(char, '');
}
var parts:Array<String> = path.split('/');
var sanitized:Array<String> = new Array<String>();
final parts:Array<String> = FileUtil.INVALID_CHARS.replace(path, '').split('/');
final sanitized:Array<String> = new Array<String>();
for (part in parts)
{
switch (part)
@@ -1196,25 +1266,31 @@ class FileUtilSandboxed
case '..':
sanitized.pop();
default:
sanitized.push(part);
sanitized.push(part.trim());
}
}
var ret:String = sanitized.join('/');
if (sanitized.length == 0)
{
#if sys
return FileUtil.gameDirectory;
#else
return '';
#end
}
#if sys
var realPath:String = Path.normalize(sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, ret])));
// ^ TODO: figure out how to get "real" path of symlinked paths
if (!realPath.startsWith(FileUtil.gameDirectory.toString()))
// TODO: figure out how to get "real" path of symlinked paths
final realPath:String = sys.FileSystem.fullPath(Path.join([FileUtil.gameDirectory, sanitized.join('/')]));
if (!realPath.startsWith(FileUtil.gameDirectory))
{
return FileUtil.gameDirectory;
}
ret = realPath;
return realPath;
#else
return sanitized.join('/');
#end
return ret;
}
/**
@@ -1222,9 +1298,9 @@ class FileUtilSandboxed
* @param path The path to check.
* @return Whether the path is protected.
*/
public static function isProtected(path:String):Bool
public static function isProtected(path:String, sanitizeFirst:Bool = true):Bool
{
path = sanitizePath(path);
if (sanitizeFirst) path = sanitizePath(path);
@:privateAccess for (protected in FileUtil.PROTECTED_PATHS)
{
if (path == protected || (protected.contains('*') && path.startsWith(protected.substring(0, protected.indexOf('*')))))
@@ -1301,7 +1377,7 @@ class FileUtilSandboxed
public static function saveFilesAsZIPToPath(resources:Array<Entry>, path:String, mode:FileWriteMode = Skip):Bool
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
return FileUtil.saveFilesAsZIPToPath(resources, path, mode);
}
@@ -1332,32 +1408,32 @@ class FileUtilSandboxed
public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
FileUtil.writeStringToPath(path, data, mode);
}
public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
FileUtil.writeBytesToPath(path, data, mode);
}
public static function appendStringToPath(path:String, data:String):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot write to protected path: $path';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot write to protected path: $path';
FileUtil.appendStringToPath(path, data);
}
public static function moveFile(path:String, destination:String):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: $path';
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: $destination';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot move protected path: $path';
if (isProtected(destination = sanitizePath(destination), false)) throw 'Cannot move to protected path: $destination';
FileUtil.moveFile(path, destination);
}
public static function deleteFile(path:String):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: $path';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot delete protected path: $path';
FileUtil.deleteFile(path);
}
@@ -1393,14 +1469,14 @@ class FileUtilSandboxed
public static function moveDir(path:String, destination:String, ?ignore:Array<String>, strict:Bool = true):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot move protected path: "$path"';
if (isProtected(destination = sanitizePath(destination))) throw 'Cannot move to protected path: "$destination"';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot move protected path: "$path"';
if (isProtected(destination = sanitizePath(destination), false)) throw 'Cannot move to protected path: "$destination"';
FileUtil.moveDir(path, destination, ignore, strict);
}
public static function deleteDir(path:String, recursive:Bool = false, ?ignore:Array<String>):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot delete protected path: "$path"';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot delete protected path: "$path"';
FileUtil.deleteDir(path, recursive, ignore);
}
@@ -1416,7 +1492,7 @@ class FileUtilSandboxed
public static function rename(path:String, newName:String, keepExtension:Bool = true):Void
{
if (isProtected(path = sanitizePath(path))) throw 'Cannot rename protected path: "$path"';
if (isProtected(path = sanitizePath(path), false)) throw 'Cannot rename protected path: "$path"';
FileUtil.rename(path, sanitizePath(newName), keepExtension);
}
@@ -1445,9 +1521,14 @@ class FileUtilSandboxed
return FileUtil.makeZIPEntryFromBytes(name, data);
}
public static function openFolder(pathFolder:String)
public static function openFolder(pathFolder:String):Void
{
FileUtil.openFolder(pathFolder);
FileUtil.openFolder(sanitizePath(pathFolder));
}
public static function openSelectFile(path:String):Void
{
FileUtil.openSelectFile(sanitizePath(path));
}
}
+4 -4
View File
@@ -17,10 +17,10 @@ class MemoryUtil
{
#if cpp
var result:String = 'HXCPP-Immix:';
result += '\n- Memory Used: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_USAGE)} bytes';
result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes';
result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes';
result += '\n- Memory Large Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_LARGE)} bytes';
result += '\n- Memory Used: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_USAGE)} bytes';
result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes';
result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes';
result += '\n- Memory Large Pool: ${cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_LARGE)} bytes';
result += '\n- HXCPP Debugger: ${#if HXCPP_DEBUGGER 'Enabled' #else 'Disabled' #end}';
result += '\n- HXCPP Exp Generational Mode: ${#if HXCPP_GC_GENERATIONAL 'Enabled' #else 'Disabled' #end}';
result += '\n- HXCPP Exp Moving GC: ${#if HXCPP_GC_MOVING 'Enabled' #else 'Disabled' #end}';
+39 -48
View File
@@ -16,6 +16,39 @@ using StringTools;
#end
class WindowUtil
{
/**
* A regex to match valid URLs.
*/
public static final URL_REGEX:EReg = ~/^https?:\/?\/?(?:www\.)?[-a-zA-Z0-9@:%_\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/;
/**
* Sanitizes a URL via a regex.
*
* @param targetUrl The URL to sanitize.
* @return The sanitized URL, or an empty string if the URL is invalid.
*/
public static function sanitizeURL(targetUrl:String):String
{
targetUrl = (targetUrl ?? '').trim();
if (targetUrl == '')
{
return '';
}
final lowerUrl:String = targetUrl.toLowerCase();
if (!lowerUrl.startsWith('http:') && !lowerUrl.startsWith('https:'))
{
targetUrl = 'http://' + targetUrl;
}
if (URL_REGEX.match(targetUrl))
{
return URL_REGEX.matched(0);
}
return '';
}
/**
* Runs platform-specific code to open a URL in a web browser.
* @param targetUrl The URL to open.
@@ -28,6 +61,12 @@ class WindowUtil
else if (protocol[0] != 'http' && protocol[0] != 'https') throw "openURL can only open http and https links.";
#if FEATURE_OPEN_URL
targetUrl = sanitizeURL(targetUrl);
if (targetUrl == '')
{
throw 'Invalid URL: "$targetUrl"';
}
#if linux
Sys.command('/usr/bin/xdg-open $targetUrl &');
#else
@@ -63,54 +102,6 @@ class WindowUtil
}
#end
/**
* Runs platform-specific code to open a path in the file explorer.
* @param targetPath The path to open.
*/
public static function openFolder(targetPath:String):Void
{
#if html5
throw 'Cannot open URLs on this platform.';
#else
if (!sys.FileSystem.exists(targetPath) || !sys.FileSystem.isDirectory(targetPath)) throw 'openFolder should only be used to open existing folders.';
#if FEATURE_OPEN_URL
#if windows
Sys.command('explorer', [targetPath.replace('/', '\\')]);
#elseif mac
Sys.command('open', [targetPath]);
#elseif linux
Sys.command('open', [targetPath]);
#end
#else
throw 'Cannot open URLs on this platform.';
#end
#end
}
/**
* Runs platform-specific code to open a file explorer and select a specific file.
* @param targetPath The path of the file to select.
*/
public static function openSelectFile(targetPath:String):Void
{
#if html5
throw 'Cannot open URLs on this platform.';
#else
#if FEATURE_OPEN_URL
#if windows
Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]);
#elseif mac
Sys.command('open', ['-R', targetPath]);
#elseif linux
// TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file.
Sys.command('open', [targetPath]);
#end
#else
throw 'Cannot open URLs on this platform.';
#end
#end
}
/**
* Dispatched when the game window is closed.
*/
@@ -31,4 +31,11 @@ class VolumePlugin extends FlxBasic
else if (PlayerSettings.player1.controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
}
}
override public function destroy():Void
{
if (FlxG.plugins.list.contains(this)) FlxG.plugins.remove(this);
super.destroy();
}
}