mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-08-30 10:25:00 +00:00
464 lines
14 KiB
Haxe
464 lines
14 KiB
Haxe
package funkin;
|
|
|
|
import flixel.graphics.FlxGraphic;
|
|
import flixel.FlxG;
|
|
import funkin.play.notes.notestyle.NoteStyle;
|
|
import openfl.utils.AssetType;
|
|
import openfl.Assets;
|
|
import openfl.system.System;
|
|
import openfl.media.Sound;
|
|
import lime.app.Future;
|
|
import lime.app.Promise;
|
|
|
|
/**
|
|
* Handles caching of textures and sounds for the game.
|
|
* TODO: Remove this once Eric finishes the memory system.
|
|
*/
|
|
@:nullSafety
|
|
class FunkinMemory
|
|
{
|
|
static var permanentCachedTextures:Map<String, FlxGraphic> = [];
|
|
static var currentCachedTextures:Map<String, FlxGraphic> = [];
|
|
static var previousCachedTextures:Map<String, FlxGraphic> = [];
|
|
|
|
// waow
|
|
static var permanentCachedSounds:Map<String, Sound> = [];
|
|
static var currentCachedSounds:Map<String, Sound> = [];
|
|
static var previousCachedSounds:Map<String, Sound> = [];
|
|
|
|
static var purgeFilter:Array<String> = ["/week", "/characters", "/charSelect", "/results"];
|
|
|
|
/**
|
|
* Caches textures that are always required.
|
|
*/
|
|
public static inline function initialCache():Void
|
|
{
|
|
var allImages:Array<String> = Assets.list();
|
|
|
|
for (file in allImages)
|
|
{
|
|
if (!(file.endsWith(".png") #if FEATURE_COMPRESSED_TEXTURES || file.endsWith(".astc") #end)
|
|
|| file.contains("chart-editor")
|
|
|| !file.contains("ui/"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
file = file.replace(" ", ""); // Handle stray spaces.
|
|
|
|
if (file.contains("shared") || Assets.exists('shared:$file', AssetType.IMAGE))
|
|
{
|
|
file = 'shared:$file';
|
|
}
|
|
permanentCacheTexture(file);
|
|
}
|
|
|
|
permanentCacheTexture(Paths.image("healthBar"));
|
|
permanentCacheTexture(Paths.image("menuDesat"));
|
|
permanentCacheTexture(Paths.image("notes", "shared"));
|
|
permanentCacheTexture(Paths.image("noteSplashes", "shared"));
|
|
permanentCacheTexture(Paths.image("noteStrumline", "shared"));
|
|
permanentCacheTexture(Paths.image("NOTE_hold_assets"));
|
|
// dude
|
|
permanentCacheTexture(Paths.image("fonts/bold", null));
|
|
permanentCacheTexture(Paths.image("fonts/default", null));
|
|
permanentCacheTexture(Paths.image("fonts/freeplay-clear", null));
|
|
|
|
var allSounds:Array<String> = Assets.list(AssetType.SOUND);
|
|
|
|
for (file in allSounds)
|
|
{
|
|
if (!file.endsWith(".ogg") || !file.contains("countdown/")) continue;
|
|
|
|
file = file.replace(" ", "");
|
|
|
|
if (file.contains("shared") || Assets.exists('shared:$file', AssetType.SOUND))
|
|
{
|
|
file = 'shared:$file';
|
|
}
|
|
|
|
permanentCacheSound(file);
|
|
}
|
|
|
|
permanentCacheSound(Paths.sound("cancelMenu"));
|
|
permanentCacheSound(Paths.sound("confirmMenu"));
|
|
permanentCacheSound(Paths.sound("screenshot"));
|
|
permanentCacheSound(Paths.sound("scrollMenu"));
|
|
permanentCacheSound(Paths.sound("soundtray/Voldown"));
|
|
permanentCacheSound(Paths.sound("soundtray/VolMAX"));
|
|
permanentCacheSound(Paths.sound("soundtray/Volup"));
|
|
permanentCacheSound(Paths.music("freakyMenu/freakyMenu"));
|
|
permanentCacheSound(Paths.music("offsetsLoop/offsetsLoop"));
|
|
permanentCacheSound(Paths.music("offsetsLoop/drumsLoop"));
|
|
permanentCacheSound(Paths.sound("missnote1", "shared"));
|
|
permanentCacheSound(Paths.sound("missnote2", "shared"));
|
|
permanentCacheSound(Paths.sound("missnote3", "shared"));
|
|
}
|
|
|
|
/**
|
|
* Clears the current texture and sound caches.
|
|
*/
|
|
public static inline function purgeCache(callGarbageCollector:Bool = false):Void
|
|
{
|
|
preparePurgeTextureCache();
|
|
purgeTextureCache();
|
|
preparePurgeSoundCache();
|
|
purgeSoundCache();
|
|
#if (cpp || neko || hl)
|
|
if (callGarbageCollector) funkin.util.MemoryUtil.collect(true);
|
|
#end
|
|
}
|
|
|
|
///// TEXTURES /////
|
|
|
|
/**
|
|
* Determine whether the texture with the given key is cached.
|
|
* @param key The key of the texture to check.
|
|
* @return Whether the texture is cached.
|
|
*/
|
|
public static function isTextureCached(key:String):Bool
|
|
{
|
|
return FlxG.bitmap.get(key) != null;
|
|
}
|
|
|
|
/**
|
|
* Ensures a texture with the given key is cached.
|
|
* @param key The key of the texture to cache.
|
|
*/
|
|
public static function cacheTexture(key:String):Void
|
|
{
|
|
if (currentCachedTextures.exists(key)) return;
|
|
|
|
if (previousCachedTextures.exists(key))
|
|
{
|
|
// Move the texture from the previous cache to the current cache.
|
|
var graphic:Null<FlxGraphic> = previousCachedTextures.get(key);
|
|
previousCachedTextures.remove(key);
|
|
if (graphic != null) currentCachedTextures.set(key, graphic);
|
|
return;
|
|
}
|
|
|
|
var graphic:Null<FlxGraphic> = FlxGraphic.fromAssetKey(key, false, null, true);
|
|
if (graphic == null)
|
|
{
|
|
FlxG.log.warn('Failed to cache graphic: $key');
|
|
return;
|
|
}
|
|
|
|
trace('Successfully cached graphic: $key');
|
|
graphic.persist = true;
|
|
currentCachedTextures.set(key, graphic);
|
|
forceRender(graphic);
|
|
}
|
|
|
|
/**
|
|
* Permanently caches a texture with the given key.
|
|
* @param key The key of the texture to cache.
|
|
*/
|
|
static function permanentCacheTexture(key:String):Void
|
|
{
|
|
if (permanentCachedTextures.exists(key)) return;
|
|
|
|
var graphic:Null<FlxGraphic> = FlxGraphic.fromAssetKey(key, false, null, true);
|
|
if (graphic == null)
|
|
{
|
|
FlxG.log.warn('Failed to cache graphic: $key');
|
|
return;
|
|
}
|
|
|
|
trace('Successfully cached graphic: $key');
|
|
graphic.persist = true;
|
|
permanentCachedTextures.set(key, graphic);
|
|
forceRender(graphic);
|
|
currentCachedTextures = permanentCachedTextures;
|
|
}
|
|
|
|
/**
|
|
* Forces the GPU to load and upload a FlxGraphic.
|
|
*/
|
|
private static function forceRender(graphic:FlxGraphic):Void
|
|
{
|
|
if (graphic == null) return;
|
|
|
|
var bmp:Null<FlxGraphic> = FlxG.bitmap.get(graphic.key);
|
|
if (bmp != null && bmp.bitmap != null) var _:Int = bmp.bitmap.width; // Trigger
|
|
|
|
// Draws sprite and actually caches it.
|
|
var sprite = new flixel.FlxSprite();
|
|
sprite.loadGraphic(graphic);
|
|
sprite.draw(); // Draw sprite and load it into game's memory.
|
|
sprite.destroy();
|
|
}
|
|
|
|
/**
|
|
* Checks, if graphic with given path cached in memory.
|
|
*/
|
|
public static inline function isGraphicCached(path:String):Bool
|
|
return permanentCachedTextures.exists(path) || currentCachedTextures.exists(path) || previousCachedTextures.exists(path);
|
|
|
|
public static function getCachedGraphic(path:String):Null<FlxGraphic>
|
|
{
|
|
if (permanentCachedTextures.exists(path)) return permanentCachedTextures.get(path);
|
|
if (currentCachedTextures.exists(path)) return currentCachedTextures.get(path);
|
|
if (previousCachedTextures.exists(path)) return previousCachedTextures.get(path); // just in case
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Prepares the cache for purging unused textures.
|
|
*/
|
|
public inline static function preparePurgeTextureCache():Void
|
|
{
|
|
previousCachedTextures = currentCachedTextures;
|
|
|
|
for (graphicKey in previousCachedTextures.keys())
|
|
{
|
|
if (permanentCachedTextures.exists(graphicKey))
|
|
{
|
|
previousCachedTextures.remove(graphicKey);
|
|
}
|
|
}
|
|
|
|
currentCachedTextures = permanentCachedTextures;
|
|
}
|
|
|
|
/**
|
|
* Purges unused textures from the cache.
|
|
*/
|
|
public static function purgeTextureCache():Void
|
|
{
|
|
for (graphicKey in previousCachedTextures.keys())
|
|
{
|
|
if (permanentCachedTextures.exists(graphicKey))
|
|
{
|
|
previousCachedTextures.remove(graphicKey);
|
|
continue;
|
|
}
|
|
|
|
if (graphicKey.contains("fonts")) continue;
|
|
|
|
var graphic:Null<FlxGraphic> = previousCachedTextures.get(graphicKey);
|
|
if (graphic != null)
|
|
{
|
|
FlxG.bitmap.remove(graphic);
|
|
graphic.destroy();
|
|
previousCachedTextures.remove(graphicKey);
|
|
Assets.cache.clear(graphicKey);
|
|
}
|
|
}
|
|
@:privateAccess
|
|
if (FlxG.bitmap._cache == null)
|
|
{
|
|
@:privateAccess
|
|
FlxG.bitmap._cache = new Map();
|
|
}
|
|
|
|
@:privateAccess
|
|
for (key in FlxG.bitmap._cache.keys())
|
|
{
|
|
var obj:Null<FlxGraphic> = FlxG.bitmap.get(key);
|
|
|
|
if (obj == null || obj.persist || permanentCachedTextures.exists(key) || key.contains("fonts"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (obj.useCount > 0)
|
|
{
|
|
for (purgeEntry in purgeFilter)
|
|
{
|
|
if (key.contains(purgeEntry))
|
|
{
|
|
FlxG.bitmap.removeKey(key);
|
|
obj.destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///// NOTE STYLE //////
|
|
|
|
public static function cacheNoteStyle(style:NoteStyle):Void
|
|
{
|
|
// TODO: Texture paths should fall back to the default values.
|
|
cacheTexture(Paths.image(style.getNoteAssetPath() ?? "note"));
|
|
cacheTexture(style.getHoldNoteAssetPath() ?? "noteHold");
|
|
cacheTexture(Paths.image(style.getStrumlineAssetPath() ?? "strumline"));
|
|
cacheTexture(Paths.image(style.getSplashAssetPath() ?? "noteSplash"));
|
|
|
|
cacheTexture(Paths.image(style.getHoldCoverDirectionAssetPath(LEFT) ?? "LEFT"));
|
|
cacheTexture(Paths.image(style.getHoldCoverDirectionAssetPath(RIGHT) ?? "RIGHT"));
|
|
cacheTexture(Paths.image(style.getHoldCoverDirectionAssetPath(UP) ?? "UP"));
|
|
cacheTexture(Paths.image(style.getHoldCoverDirectionAssetPath(DOWN) ?? "DOWN"));
|
|
|
|
// cacheTexture(Paths.image(style.buildCountdownSpritePath(THREE) ?? "THREE"));
|
|
cacheTexture(Paths.image(style.buildCountdownSpritePath(TWO) ?? "TWO"));
|
|
cacheTexture(Paths.image(style.buildCountdownSpritePath(ONE) ?? "ONE"));
|
|
cacheTexture(Paths.image(style.buildCountdownSpritePath(GO) ?? "GO"));
|
|
|
|
cacheSound(style.getCountdownSoundPath(THREE) ?? "THREE");
|
|
cacheSound(style.getCountdownSoundPath(TWO) ?? "TWO");
|
|
cacheSound(style.getCountdownSoundPath(ONE) ?? "ONE");
|
|
cacheSound(style.getCountdownSoundPath(GO) ?? "GO");
|
|
|
|
cacheTexture(Paths.image(style.buildJudgementSpritePath("sick") ?? 'sick'));
|
|
cacheTexture(Paths.image(style.buildJudgementSpritePath("good") ?? 'good'));
|
|
cacheTexture(Paths.image(style.buildJudgementSpritePath("bad") ?? 'bad'));
|
|
cacheTexture(Paths.image(style.buildJudgementSpritePath("shit") ?? 'shit'));
|
|
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(0) ?? '0'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(1) ?? '1'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(2) ?? '2'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(3) ?? '3'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(4) ?? '4'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(5) ?? '5'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(6) ?? '6'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(7) ?? '7'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(8) ?? '8'));
|
|
cacheTexture(Paths.image(style.buildComboNumSpritePath(9) ?? '9'));
|
|
}
|
|
|
|
///// SOUND //////
|
|
|
|
public static function cacheSound(key:String):Void
|
|
{
|
|
if (currentCachedSounds.exists(key)) return;
|
|
|
|
if (previousCachedSounds.exists(key))
|
|
{
|
|
// Move the texture from the previous cache to the current cache.
|
|
var sound:Null<Sound> = previousCachedSounds.get(key);
|
|
previousCachedSounds.remove(key);
|
|
if (sound != null) currentCachedSounds.set(key, sound);
|
|
return;
|
|
}
|
|
|
|
var sound:Null<Sound> = Assets.getSound(key, true);
|
|
if (sound == null) return;
|
|
else
|
|
currentCachedSounds.set(key, sound);
|
|
}
|
|
|
|
public static function permanentCacheSound(key:String):Void
|
|
{
|
|
if (permanentCachedSounds.exists(key)) return;
|
|
|
|
var sound:Null<Sound> = Assets.getSound(key, true);
|
|
if (sound == null) return;
|
|
else
|
|
permanentCachedSounds.set(key, sound);
|
|
|
|
if (sound != null) currentCachedSounds.set(key, sound);
|
|
}
|
|
|
|
public static function preparePurgeSoundCache():Void
|
|
{
|
|
previousCachedSounds = currentCachedSounds;
|
|
|
|
for (key in previousCachedSounds.keys())
|
|
{
|
|
if (permanentCachedSounds.exists(key))
|
|
{
|
|
previousCachedSounds.remove(key);
|
|
}
|
|
}
|
|
|
|
currentCachedSounds = permanentCachedSounds;
|
|
}
|
|
|
|
/**
|
|
* Purges unused sounds from the cache.
|
|
*/
|
|
public static inline function purgeSoundCache():Void
|
|
{
|
|
for (key in previousCachedSounds.keys())
|
|
{
|
|
if (permanentCachedSounds.exists(key))
|
|
{
|
|
previousCachedSounds.remove(key);
|
|
continue;
|
|
}
|
|
|
|
var sound:Null<Sound> = previousCachedSounds.get(key);
|
|
if (sound != null)
|
|
{
|
|
Assets.cache.removeSound(key);
|
|
previousCachedSounds.remove(key);
|
|
}
|
|
}
|
|
Assets.cache.clear("songs");
|
|
Assets.cache.clear("music");
|
|
// Felt lazy.
|
|
var key = Paths.music("freakyMenu/freakyMenu");
|
|
var sound:Null<Sound> = Assets.getSound(key, true);
|
|
if (sound != null)
|
|
{
|
|
permanentCachedSounds.set(key, sound);
|
|
currentCachedSounds.set(key, sound);
|
|
}
|
|
}
|
|
|
|
///// MISC /////
|
|
|
|
public static inline function clearFreeplay():Void
|
|
{
|
|
var keysToRemove:Array<String> = [];
|
|
|
|
@:privateAccess
|
|
for (key in FlxG.bitmap._cache.keys())
|
|
{
|
|
if (!key.contains("freeplay")) continue;
|
|
if (permanentCachedTextures.exists(key) || key.contains("fonts")) continue;
|
|
|
|
keysToRemove.push(key);
|
|
}
|
|
|
|
@:privateAccess
|
|
for (key in keysToRemove)
|
|
{
|
|
trace('Cleaning up $key');
|
|
var obj:Null<FlxGraphic> = FlxG.bitmap.get(key);
|
|
if (obj != null)
|
|
{
|
|
obj.destroy();
|
|
}
|
|
FlxG.bitmap.removeKey(key);
|
|
if (currentCachedTextures.exists(key)) currentCachedTextures.remove(key);
|
|
Assets.cache.clear(key);
|
|
}
|
|
|
|
preparePurgeSoundCache();
|
|
purgeSoundCache();
|
|
}
|
|
|
|
public static inline function clearStickers():Void
|
|
{
|
|
var keysToRemove:Array<String> = [];
|
|
|
|
@:privateAccess
|
|
for (key in FlxG.bitmap._cache.keys())
|
|
{
|
|
if (!key.contains("stickers")) continue;
|
|
if (permanentCachedTextures.exists(key) || key.contains("fonts")) continue;
|
|
|
|
keysToRemove.push(key);
|
|
}
|
|
|
|
@:privateAccess
|
|
for (key in keysToRemove)
|
|
{
|
|
trace('Cleaning up $key');
|
|
var obj:Null<FlxGraphic> = FlxG.bitmap.get(key);
|
|
if (obj != null)
|
|
{
|
|
obj.destroy();
|
|
}
|
|
FlxG.bitmap.removeKey(key);
|
|
if (currentCachedTextures.exists(key)) currentCachedTextures.remove(key);
|
|
Assets.cache.clear(key);
|
|
}
|
|
}
|
|
}
|