I wrote some hacky little code that requires you to generate a music table from the client side so if youre running a dedicated server this will only work if you generate a music table before hand on a client side, it stores the length of each song by their crc and then allows retrieaval
client.cs
function repeatStr(%str,%num)
{
%ret = "";
for(%i=0;%i<%num;%i++)
%ret = %ret @ %str;
return %ret;
}
function createMusicLenTable()
{
$Swol_SongLenTableConfig = "config/server/songLengths.cs";
if(isFile($Swol_SongLenTableConfig))
exec($Swol_SongLenTableConfig);
%pattern = "Add-Ons/Music/*.ogg";
%file = findFirstFile(%pattern);
$Swol_SongLenTableCnt = 0;
$Swol_SongLenTableUCnt = 0;
echo("\c5Creating MusicLen table");
while(%file !$= "")
{
$Swol_SongLenTable[$Swol_SongLenTableCnt] = %file;
$Swol_SongLenTableCnt++;
%file = findNextFile(%pattern);
}
createMusicLenTableTick(0);
}
function createMusicLenTableTick(%tick)
{
cancel($Swol_SongLenTableSched);
%file = $Swol_SongLenTable[%tick];
%crc = getFileCRC(%file);
if(getSubStr(%crc,0,1) $= "-")
%crc = "M" @ getSubStr(%crc,1,99);
if($Swol_SLen[%crc] $= "")
{
%len = alxGetWaveLen(%file);
%base = fileBase(%file);
if(%len == 0)
{
echo("\c2 > BAD OGG MAY BE IN ZIP OR JUST CORRUPTED (" @ %base @ ")");
}
else
{
$Swol_SLen[%crc] = %len;
echo("\c1 > " @ %base @ repeatStr(" ",50-strLen(%base)) @ (mFloor(%len/1000)) @ "s");
}
%noSkip = 1;
$Swol_SongLenTableUCnt++;
}
if(%tick >= $Swol_SongLenTableCnt-1)
{
createMusicLenTableEnd();
return;
}
$Swol_SongLenTableSched = schedule((%noSkip ? 32 : 1),0,createMusicLenTableTick,%tick++);
}
function createMusicLenTableEnd()
{
cancel($Swol_SongLenTableSched);
echo("\c5MusicLen generation table complete");
echo(" \c5Added " @ $Swol_SongLenTableUCnt @ " song" @ ($Swol_SongLenTableUCnt != 1 ? "s" : ""));
export("$Swol_SLen*",$Swol_SongLenTableConfig);
deleteVariables("$Swol_SongLenTable*");
}
schedule(1,0,createMusicLenTable);
server.cs
function execMusicLenTable()
{
$Swol_SongLenTableConfig = "config/server/songLengths.cs";
if(isFile($Swol_SongLenTableConfig))
{
exec($Swol_SongLenTableConfig);
echo("MusicLen table found and executed");
}
else
{
echo("MusicLen table not generated");
}
}
function getMusicLength(%file)
{
%crc = getFileCRC(%file);
if(getSubStr(%crc,0,1) $= "-")
%crc = "M" @ getSubStr(%crc,1,99);
%ms = $Swol_SLen[%crc];
return %ms;
}
schedule(1,0,execMusicLenTable);
this code will run when you boot up blockland and it may hog some resources while it generates the first table, afterwards it shouldn't take as long
then on the server end it will execute the table and then you can find the length of an ogg file by using the server command, for example:
echo(getMusicLength("Add-Ons/Music/Piano_Bass.ogg"));
which will echo "6000" (fun fact piano bass is down to the millisecond exactly 6 seconds)
this is my solution if anyone wants to build on it use it etc feel free
implementing this to music loop would work something like
>store client's current ping
>get length of song you want to play
>play song on client
>start a schedule for client's ping+length of song
>at end of schedule play next song or stop song
there will obviously be some clipping issues because of fluctuating ping but you will be pretty close to when the song stops playing on the client side although you will want to do some calculations in realtime incase the server lags and offsets this schedule since the song will continue to play even if the server lags so the schedule should probably be a repeating schedule that does realtime checks
heres an example of some pseudoy code for the job
function gameConnection::onSongEnd(%cl,%emitter)
{
%cl.songsPlayingSet.remove(%emitter);
%emitter.delete(); //stop emitter?
if(%cl.songsPlayingSet.getCount() == 0)
{
realTimeSongEndTickSet.remove(%cl);
}
//do stuff
}
function gameConnection::playSongOnClient(%cl,%db)
{
%ping = %cl.getPing();
%size = getMusicLength(%db.filename);
%emitter = create audio emitter really far away
%emitter.scopeToClient(%cl);
%cl.realSongEnd[%emitter] = getRealTime()+%ping+%size;
if(!isObject(%cl.songsPlayingSet))
{
%cl.songsPlayingSet = new simSet();
}
%cl.songsPlayingSet.add(%emitter);
%set = realTimeSongEndTickSet;
%set.add(%cl);
realTimeSongEndTick();
}
function realTimeSongEndTick()
{
cancel($swol_realTimeSongEndTickSched);
%set = realTimeSongEndTickSet;
if((%c = %set.getCount()) == 0)
return;
%time = getRealTime();
for(%i=0;%i<%c;%c++)
{
%cl = %set.getObject(%i);
%sset = %cl.songsPlayingSet;
%songs = %sset.getCount();
for(%a=0;%a<%songs;%a++)
{
%song = %sset.getObject(%a);
if(getRealTime()-%cl.realSongEnd[%song] >= 0)
{
%cl.onSongEnd(%song);
}
}
}
$swol_realTimeSongEndTickSched = schedule(1000,0,realTimeSongEndTick);
}
package swol_songEnd
{
function gameConnection::onDrop(%cl)
{
if(isObject(%cl.songsPlayingSet))
%cl.songsPlayingSet.delete();
return parent::onDrop(%cl);
}
};
activatePackage(swol_songEnd);
if(!isObject(realTimeSongEndTickSet))
new simSet(realTimeSongEndTickSet);
pardon all my bad variable and function names i know theyre awful