30 Commits

Author SHA1 Message Date
  Thomas Cheyney 2726913f3d Turn debug back off, add skrillex, bump version 2 years ago
  Thomas Cheyney 9c3967f705 Turn off unreal analytics 2 years ago
  Thomas Cheyney 5e953465f3 Fix writing json as UTF16. Add new environments to dropdown 2 years ago
  Thomas Cheyney f98d0b4fac Fix characteristic names again 3 years ago
  Thomas Cheyney a05bf36ed1 Hide editor offset. Save editor scale. Fix max time auto value. 3 years ago
  Thomas Cheyney d4a279b125 Fix strobing right laser, guard against writing nothing, fix custom platforms with spaces 3 years ago
  Thomas Cheyney 13d624068d Bugfixes 3 years ago
  Thomas Cheyney 75401345f2 Boost events set light colours 3 years ago
  Thomas Cheyney acb7dd590b New environments, light colours 3 years ago
  Thomas Cheyney 2415420080 Add close button to dialog 3 years ago
  Thomas Cheyney bc804ed1e9 Add songcore colour pickers 3 years ago
  Thomas Cheyney eb2ab56213 Fix copy pasting special events 3 years ago
  Thomas Cheyney 6b5ce295d8 Upgrade map files to 2.2.0 schema 3 years ago
  Thomas Cheyney a8c49a1199 Is this... error handling? 3 years ago
  Thomas Cheyney a692c817d8 Darken default not colour to match old MMA2 builds 3 years ago
  Thomas Cheyney 33e67146ea Add basic songcore colour support, add boost events 3 years ago
  Thomas Cheyney 8b2fa3647f Add server files 3 years ago
  Thomas Cheyney 343ea27e68 Prevent crash when audio file is empty 3 years ago
  Thomas Cheyney 98cac55eae Include crash reporter, small UI tweaks 3 years ago
  Thomas Cheyney 070850e4ee Improve block placement raytracing 3 years ago
  Thomas Cheyney 384c567886 Fix offset being ignored in distance calculation 3 years ago
  Thomas Cheyney 2c87cf0339 Refactor beat numbering 3 years ago
  Qwasyx 1c956a1210 made beat numberings not depend on editor scale 3 years ago
  Thomas Cheyney 2440e9d650 Clear custom platform fields if empty, default error checker to half jump duration 3 years ago
  Thomas Cheyney 644a77a8cf Only show rename message if filename ends in ogg 3 years ago
  Thomas Cheyney 799adf9fa9 Add MP3 check 3 years ago
  Thomas Cheyney 3489c1228f Add special character and weird image type validation 3 years ago
  Thomas Cheyney 704db8756e Fake walls fixes 3 years ago
  Thomas Cheyney 8938c7c45e Save as NoArrows and OneSaber 3 years ago
  Thomas Cheyney 1553b979d8 Add extra error handling to loading songs 3 years ago
67 changed files with 1486 additions and 47 deletions
Split View
  1. +24
    -3
      Config/DefaultEngine.ini
  2. +2
    -1
      Config/DefaultGame.ini
  3. BIN
      Content/Blueprints/BPFL_editor.uasset
  4. BIN
      Content/Blueprints/BPSG_config.uasset
  5. BIN
      Content/Blueprints/BP_bookmark.uasset
  6. BIN
      Content/Blueprints/BP_controller.uasset
  7. BIN
      Content/Blueprints/BP_event.uasset
  8. BIN
      Content/Blueprints/BP_eventSelection.uasset
  9. BIN
      Content/Blueprints/BP_event_ghost.uasset
  10. BIN
      Content/Blueprints/BP_event_gradient.uasset
  11. BIN
      Content/Blueprints/BP_event_manager.uasset
  12. BIN
      Content/Blueprints/BP_lasers.uasset
  13. BIN
      Content/Blueprints/BP_note.uasset
  14. BIN
      Content/Blueprints/BP_wall.uasset
  15. BIN
      Content/Blueprints/VarTypes/StructDifficultyInfos.uasset
  16. BIN
      Content/Blueprints/VarTypes/StructSong.uasset
  17. BIN
      Content/Blueprints/VarTypes/structBeatMapSet.uasset
  18. BIN
      Content/Blueprints/WidgetEditorMenu.uasset
  19. BIN
      Content/Blueprints/WidgetExtraFields.uasset
  20. BIN
      Content/Blueprints/WidgetMapMenu.uasset
  21. BIN
      Content/Blueprints/WidgetSongInfos.uasset
  22. BIN
      Content/Blueprints/WidgetSpawnDistance.uasset
  23. BIN
      Content/Levels/Level_Startup.umap
  24. BIN
      Content/Materials/Events/MI_light_RGB.uasset
  25. BIN
      Content/Materials/Events/MI_light_RGB_ghost.uasset
  26. BIN
      Content/Materials/Events/MI_light_off.uasset
  27. BIN
      Content/Materials/Events/MI_light_on_blue.uasset
  28. BIN
      Content/Materials/Events/MI_light_on_blue_fade.uasset
  29. BIN
      Content/Materials/Events/MI_light_on_blue_flash.uasset
  30. BIN
      Content/Materials/Events/MI_light_on_red.uasset
  31. BIN
      Content/Materials/Events/MI_light_on_red_fade.uasset
  32. BIN
      Content/Materials/Events/MI_light_on_red_flash.uasset
  33. BIN
      Content/Materials/Events/M_light_event.uasset
  34. BIN
      Content/Materials/Events/ghostbluefade.uasset
  35. BIN
      Content/Materials/Events/ghostblueflash.uasset
  36. BIN
      Content/Materials/Events/ghostblueon.uasset
  37. BIN
      Content/Materials/Events/ghostredfade.uasset
  38. BIN
      Content/Materials/Events/ghostredflash.uasset
  39. BIN
      Content/Materials/Events/ghostredon.uasset
  40. BIN
      Content/Materials/MI_boost.uasset
  41. BIN
      Content/Materials/M_CubeNote.uasset
  42. BIN
      Content/Materials/M_CubeNote_red.uasset
  43. BIN
      Content/Materials/M_bomb_ghost.uasset
  44. BIN
      Content/Meshes/Cube.uasset
  45. BIN
      Content/Meshes/untitled.uasset
  46. BIN
      Content/Textures/Icons/VisionBlockCat.uasset
  47. BIN
      Content/Textures/Icons/bigcat.uasset
  48. BIN
      Content/Textures/Icons/donateCat.uasset
  49. BIN
      Content/Textures/Icons/fileextensionimage.uasset
  50. BIN
      Content/Textures/Icons/good_mapping_practice.uasset
  51. BIN
      Content/Textures/Icons/pfp.uasset
  52. BIN
      Content/Textures/Icons/visionblock1.uasset
  53. BIN
      Content/Textures/Icons/visionblock2.uasset
  54. BIN
      Content/Textures/Icons/visionblock3.uasset
  55. BIN
      Content/Textures/Icons/visionblockcatfull.uasset
  56. +28
    -24
      Plugins/eXiSoundVis/Source/eXiSoundVis/Private/AudioDecompressWorker.cpp
  57. +19
    -13
      Plugins/eXiSoundVis/Source/eXiSoundVis/Private/SoundVisComponent.cpp
  58. +5
    -3
      Plugins/eXiSoundVis/Source/eXiSoundVis/Public/SoundVisComponent.h
  59. +3
    -1
      Plugins/eXiSoundVis/Source/eXiSoundVis/eXiSoundVis.Build.cs
  60. +44
    -0
      Server/ServerReadme.md
  61. +7
    -0
      Server/config.json
  62. +346
    -0
      Server/package-lock.json
  63. +15
    -0
      Server/package.json
  64. +730
    -0
      Server/server.js
  65. +249
    -0
      Source/MediocreMapAssistant2/BPFileIO.cpp
  66. +13
    -1
      Source/MediocreMapAssistant2/BPFileIO.h
  67. +1
    -1
      Source/MediocreMapAssistant2/RenderWaveform.cpp

+ 24
- 3
Config/DefaultEngine.ini View File

@ -114,9 +114,6 @@ SmoothedFrameRateRange=(LowerBound=(Type=Inclusive,Value=22.000000),UpperBound=(
+ActiveGameNameRedirects=(OldGameName="MediocreMapper",NewGameName="MediocreMapAssistant2")
bSmoothFrameRate=True
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
Compiler=VisualStudio2017
[/Script/Engine.CollisionProfile]
-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
@ -187,6 +184,30 @@ Compiler=VisualStudio2017
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
bGenerateCrashReportSymbols=True
[CrashReportClient]
CrashReportClientVersion=4.7.1
DataRouterUrl="https://o462013.ingest.sentry.io/api/5464708/unreal/40d9818b9fee4b72be61bcd0cc2c5e3b/"
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
Compiler=VisualStudio2017
-TargetedRHIs=PCD3D_SM5
-TargetedRHIs=PCD3D_SM4
+TargetedRHIs=PCD3D_SM5
+TargetedRHIs=PCD3D_SM4
MinimumOSVersion=MSOS_Vista
AudioDevice=
AudioSampleRate=48000
AudioCallbackBufferFrameSize=1024
AudioNumBuffersToEnqueue=1
AudioMaxChannels=0
AudioNumSourceWorkers=4
SpatializationPlugin=
ReverbPlugin=
OcclusionPlugin=
[/Script/Engine.EndUserSettings]
bSendAnonymousUsageDataToEpic=False
[/Script/Engine.PhysicsSettings]
DefaultGravityZ=-980.000000
DefaultTerminalVelocity=4000.000000


+ 2
- 1
Config/DefaultGame.ini View File

@ -3,7 +3,7 @@ ProjectID=F5529D354BF9DDA5B4BC06BB62D10933
bShouldWindowPreserveAspectRatio=False
ProjectName=MediocreMapAssistant2
ProjectDisplayedTitle=NSLOCTEXT("[/Script/EngineSettings]", "A4F171D24A09E6B0C435A39031D526F5", "Mediocre Map Assistant 2")
ProjectVersion=4.4.0
ProjectVersion=4.8.4
[/Script/UnrealEd.ProjectPackagingSettings]
Build=IfProjectHasCode
@ -39,3 +39,4 @@ bSkipEditorContent=True
+MapsToCook=(FilePath="/Game/Levels/Level_Startup")
bNativizeBlueprintAssets=False
bNativizeOnlySelectedBlueprints=False

BIN
Content/Blueprints/BPFL_editor.uasset View File


BIN
Content/Blueprints/BPSG_config.uasset View File


BIN
Content/Blueprints/BP_bookmark.uasset View File


BIN
Content/Blueprints/BP_controller.uasset View File


BIN
Content/Blueprints/BP_event.uasset View File


BIN
Content/Blueprints/BP_eventSelection.uasset View File


BIN
Content/Blueprints/BP_event_ghost.uasset View File


BIN
Content/Blueprints/BP_event_gradient.uasset View File


BIN
Content/Blueprints/BP_event_manager.uasset View File


BIN
Content/Blueprints/BP_lasers.uasset View File


BIN
Content/Blueprints/BP_note.uasset View File


BIN
Content/Blueprints/BP_wall.uasset View File


BIN
Content/Blueprints/VarTypes/StructDifficultyInfos.uasset View File


BIN
Content/Blueprints/VarTypes/StructSong.uasset View File


BIN
Content/Blueprints/VarTypes/structBeatMapSet.uasset View File


BIN
Content/Blueprints/WidgetEditorMenu.uasset View File


BIN
Content/Blueprints/WidgetExtraFields.uasset View File


BIN
Content/Blueprints/WidgetMapMenu.uasset View File


BIN
Content/Blueprints/WidgetSongInfos.uasset View File


BIN
Content/Blueprints/WidgetSpawnDistance.uasset View File


BIN
Content/Levels/Level_Startup.umap View File


BIN
Content/Materials/Events/MI_light_RGB.uasset View File


BIN
Content/Materials/Events/MI_light_RGB_ghost.uasset View File


BIN
Content/Materials/Events/MI_light_off.uasset View File


BIN
Content/Materials/Events/MI_light_on_blue.uasset View File


BIN
Content/Materials/Events/MI_light_on_blue_fade.uasset View File


BIN
Content/Materials/Events/MI_light_on_blue_flash.uasset View File


BIN
Content/Materials/Events/MI_light_on_red.uasset View File


BIN
Content/Materials/Events/MI_light_on_red_fade.uasset View File


BIN
Content/Materials/Events/MI_light_on_red_flash.uasset View File


BIN
Content/Materials/Events/M_light_event.uasset View File


BIN
Content/Materials/Events/ghostbluefade.uasset View File


BIN
Content/Materials/Events/ghostblueflash.uasset View File


BIN
Content/Materials/Events/ghostblueon.uasset View File


BIN
Content/Materials/Events/ghostredfade.uasset View File


BIN
Content/Materials/Events/ghostredflash.uasset View File


BIN
Content/Materials/Events/ghostredon.uasset View File


BIN
Content/Materials/MI_boost.uasset View File


BIN
Content/Materials/M_CubeNote.uasset View File


BIN
Content/Materials/M_CubeNote_red.uasset View File


BIN
Content/Materials/M_bomb_ghost.uasset View File


BIN
Content/Meshes/Cube.uasset View File


BIN
Content/Meshes/untitled.uasset View File


BIN
Content/Textures/Icons/VisionBlockCat.uasset View File


BIN
Content/Textures/Icons/bigcat.uasset View File


BIN
Content/Textures/Icons/donateCat.uasset View File


BIN
Content/Textures/Icons/fileextensionimage.uasset View File


BIN
Content/Textures/Icons/good_mapping_practice.uasset View File


BIN
Content/Textures/Icons/pfp.uasset View File


BIN
Content/Textures/Icons/visionblock1.uasset View File


BIN
Content/Textures/Icons/visionblock2.uasset View File


BIN
Content/Textures/Icons/visionblock3.uasset View File


BIN
Content/Textures/Icons/visionblockcatfull.uasset View File


+ 28
- 24
Plugins/eXiSoundVis/Source/eXiSoundVis/Private/AudioDecompressWorker.cpp View File

@ -67,37 +67,41 @@ uint32 FAudioDecompressWorker::Run()
if (AudioInfo != NULL)
{
FSoundQualityInfo QualityInfo = { 0 };
try {
FSoundQualityInfo QualityInfo = { 0 };
// Parse the audio header for the relevant information
if (!(SoundWaveRef->ResourceData))
{
return 0;
}
// Parse the audio header for the relevant information
if (!(SoundWaveRef->ResourceData))
{
return 0;
}
if (AudioInfo->ReadCompressedInfo(SoundWaveRef->ResourceData, SoundWaveRef->ResourceSize, &QualityInfo))
{
FScopeCycleCounterUObject WaveObject(SoundWaveRef);
if (AudioInfo->ReadCompressedInfo(SoundWaveRef->ResourceData, SoundWaveRef->ResourceSize, &QualityInfo))
{
FScopeCycleCounterUObject WaveObject(SoundWaveRef);
// Extract the data
SoundWaveRef->SampleRate = QualityInfo.SampleRate;
SoundWaveRef->NumChannels = QualityInfo.NumChannels;
// Extract the data
SoundWaveRef->SampleRate = QualityInfo.SampleRate;
SoundWaveRef->NumChannels = QualityInfo.NumChannels;
if (QualityInfo.Duration > 0.0f)
{
SoundWaveRef->Duration = QualityInfo.Duration;
}
if (QualityInfo.Duration > 0.0f)
{
SoundWaveRef->Duration = QualityInfo.Duration;
}
const uint32 PCMBufferSize = SoundWaveRef->Duration * SoundWaveRef->SampleRate * SoundWaveRef->NumChannels;
const uint32 PCMBufferSize = SoundWaveRef->Duration * SoundWaveRef->SampleRate * SoundWaveRef->NumChannels;
SoundWaveRef->CachedRealtimeFirstBuffer = new uint8[PCMBufferSize * 2];
SoundWaveRef->CachedRealtimeFirstBuffer = new uint8[PCMBufferSize * 2];
AudioInfo->SeekToTime(0.0f);
AudioInfo->ReadCompressedData(SoundWaveRef->CachedRealtimeFirstBuffer, false, PCMBufferSize * 2);
}
else if (SoundWaveRef->DecompressionType == DTYPE_RealTime || SoundWaveRef->DecompressionType == DTYPE_Native)
{
SoundWaveRef->RemoveAudioResource();
AudioInfo->SeekToTime(0.0f);
AudioInfo->ReadCompressedData(SoundWaveRef->CachedRealtimeFirstBuffer, false, PCMBufferSize * 2);
}
else if (SoundWaveRef->DecompressionType == DTYPE_RealTime || SoundWaveRef->DecompressionType == DTYPE_Native)
{
SoundWaveRef->RemoveAudioResource();
}
} catch (const std::exception&) {
// Yo, bad things happened
}
delete AudioInfo;


+ 19
- 13
Plugins/eXiSoundVis/Source/eXiSoundVis/Private/SoundVisComponent.cpp View File

@ -41,7 +41,7 @@ void USoundVisComponent::TickComponent(float DeltaTime, ELevelTick TickType, FAc
/// Functions to load Data from the HardDrive
void USoundVisComponent::LoadSoundFileFromHD(const FString& InFilePath)
bool USoundVisComponent::LoadSoundFileFromHD(const FString& InFilePath)
{
// Create new SoundWave Object
CompressedSoundWaveRef = NewObject<USoundWave>(USoundWave::StaticClass());
@ -50,7 +50,7 @@ void USoundVisComponent::LoadSoundFileFromHD(const FString& InFilePath)
if (!CompressedSoundWaveRef) {
PrintError(TEXT("Failed to create new SoundWave Object!"));
return;
return false;
}
// If true, the Sound was successfully loaded
@ -64,6 +64,7 @@ void USoundVisComponent::LoadSoundFileFromHD(const FString& InFilePath)
if (bLoaded)
{
UE_LOG(LogTemp, Error, TEXT("LoadSoundFileFromHD 0"));
// Fill the SoundData into the SoundWave Object
if (RawFile.Num() > 0) {
@ -92,11 +93,11 @@ void USoundVisComponent::LoadSoundFileFromHD(const FString& InFilePath)
if (!bLoaded) {
PrintError(TEXT("Something went wrong while loading the Sound Data!"));
return;
return false;
}
// Fill the PCMSampleBuffer
GetPCMDataFromFile(CompressedSoundWaveRef);
return GetPCMDataFromFile(CompressedSoundWaveRef);
}
bool USoundVisComponent::FillSoundWaveInfo(USoundWave* InSoundWave, TArray<uint8>* InRawFile)
@ -124,18 +125,18 @@ bool USoundVisComponent::FillSoundWaveInfo(USoundWave* InSoundWave, TArray<uint8
/// Function to decompress the compressed Data that comes with the .ogg file
void USoundVisComponent::GetPCMDataFromFile(USoundWave* InSoundWave)
bool USoundVisComponent::GetPCMDataFromFile(USoundWave* InSoundWave)
{
if (InSoundWave == nullptr) {
PrintError(TEXT("Passed SoundWave pointer is a nullptr!"));
return;
return false;
}
if (InSoundWave->NumChannels < 1 || InSoundWave->NumChannels > 2) {
PrintError(TEXT("SoundWave Object has not the right amount of Channels. Plugin only supports 1 or 2!"));
return;
return false;
}
if (GEngine)
@ -151,13 +152,14 @@ void USoundVisComponent::GetPCMDataFromFile(USoundWave* InSoundWave)
// Creates a new DecompressWorker and starts it
InitNewDecompressTask(InSoundWave);
return true;
}
else {
PrintError(TEXT("Couldn't get a valid Pointer to the Main AudioDevice!"));
return;
}
}
return false;
}
void USoundVisComponent::CalculateFrequencySpectrum(USoundWave* InSoundWaveRef, const float InStartTime, const float InDuration, TArray<float>& OutFrequencies)
@ -349,8 +351,12 @@ void USoundVisComponent::Notify_SoundDecompressed()
// ..clear the timer and..
GetWorld()->GetTimerManager().ClearTimer(AudioDecompressTimer);
//..broadcast the result to the Blueprint
OnFileLoadCompleted.Broadcast(FAudioDecompressWorker::Runnable->GetSoundWaveRef());
try {
//..broadcast the result to the Blueprint
OnFileLoadCompleted.Broadcast(FAudioDecompressWorker::Runnable->GetSoundWaveRef());
} catch (const std::exception&) {
// Yo, bad things happened
}
PrintLog(TEXT("Worker finished!"));
}
@ -399,9 +405,9 @@ void USoundVisComponent::HandleFrequencySpectrumCalculation()
/// Blueprint Versions of the File Data Functions
void USoundVisComponent::BP_LoadSoundFileFromHD(const FString InFilePath)
bool USoundVisComponent::BP_LoadSoundFileFromHD(const FString InFilePath)
{
LoadSoundFileFromHD(InFilePath);
return LoadSoundFileFromHD(InFilePath);
}
void USoundVisComponent::BP_LoadAllSoundFileNamesFromHD(bool& bLoaded, const FString InDirectoryPath, const bool bInAbsolutePath, const FString InFileExtension, TArray<FString>& OutSoundFileNamesWithPath, TArray<FString>& OutSoundFileNamesWithoutPath)
@ -653,4 +659,4 @@ void USoundVisComponent::BP_GetAverageFrequencyValueInRange(USoundWave* InSoundW
}
OutAverageFrequency = ValueSum / NumberOfFrequencies;
}
}

+ 5
- 3
Plugins/eXiSoundVis/Source/eXiSoundVis/Public/SoundVisComponent.h View File

@ -134,14 +134,14 @@ public:
/// Functions to load Data from the HardDrive
// Function to load a sound file from the HD
void LoadSoundFileFromHD(const FString& InFilePath);
bool LoadSoundFileFromHD(const FString& InFilePath);
// Function to fill in the RawFile sound data into the USoundWave object
bool FillSoundWaveInfo(class USoundWave* InSoundWave, TArray<uint8>* InRawFile);
/// Function to decompress the compressed Data that comes with the .ogg file
void GetPCMDataFromFile(class USoundWave* InSoundWave);
bool GetPCMDataFromFile(class USoundWave* InSoundWave);
/// Function to calculate the frequency spectrum
@ -158,6 +158,8 @@ public:
// DEBUG Test function to check if Task can call stuff in here
void Notify_SoundDecompressed();
void Notify_FailedToDecompress();
// Function that is looped to handle the calculation of the FrequencySpectrum
UFUNCTION()
void HandleFrequencySpectrumCalculation();
@ -171,7 +173,7 @@ public:
*
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Load Sound File"), Category = "SoundVis | SoundFile")
void BP_LoadSoundFileFromHD(const FString InFilePath);
bool BP_LoadSoundFileFromHD(const FString InFilePath);
/**
* Will get an Array of Names of the Found SoundFiles


+ 3
- 1
Plugins/eXiSoundVis/Source/eXiSoundVis/eXiSoundVis.Build.cs View File

@ -5,7 +5,9 @@ public class eXiSoundVis : ModuleRules
{
public eXiSoundVis(ReadOnlyTargetRules Target) : base(Target)
{
PrivateIncludePaths.AddRange(new string[] { "eXiSoundVis/Private" });
this.bEnableExceptions = true;
PrivateIncludePaths.AddRange(new string[] { "eXiSoundVis/Private" });
PublicIncludePaths.AddRange(new string[] { "eXiSoundVis/Public" });
PublicDependencyModuleNames.AddRange(new string[] { "Engine", "Core", "CoreUObject", "InputCore", "RHI", "Kiss_FFT" });


+ 44
- 0
Server/ServerReadme.md View File

@ -0,0 +1,44 @@
# Mediocre Mapper Multi-Mapper Server setup
## What you need to start
* install node.js from https://nodejs.org/en/
* move your song into the /Songs/ directory
* port forward a TCP port of your choosing. 17425 is default.
* create a song in MediocreMapper and set up difficulties, offset and BPM
## Setting up the server
### config.json setup
```
"folder" - Folder name of the song
"characteristic" - Characteristic to be used (Standard/No Arrows/One Saber)
"difficulty": - Difficulty to be used (Easy/Normal/Hard/Expert/ExpertPlus)
"port": - Port you'll be using
"password": - Password to the server, can be left empty for no password
```
### config.json example
```
{
"folder": "The Other Side",
"characteristic": "Standard",
"difficulty": "ExpertPlus",
"port": 17425,
"password": "967"
}
```
## Starting the server
* copy the song from your Mediocre Mapper's /CustomSongs/ Directory into your Server's /Songs/ directory
* set up the config.json as above
* open up a command line and navigate to the folder containing server.js. Or shift + right click in the folder in Windows Explorer to and open up PowerShell
* run command: `npm install`
* run command: `node ./server.js`
* all done!
## Other info
Server saves every 5 minutes, so at most 5 minutes of progress is lost in the case of a crash.
No more than one person using the same name can be in a session. sometimes this bugs out and there's an improper disconnect. Have server restarted after a save if you're unable to join with the same name and really don't wanna change it. or just change it.
Full or partial information can be supplied to the server by typing for example `node ./server.js --difficulty "ExpertPlus"`

+ 7
- 0
Server/config.json View File

@ -0,0 +1,7 @@
{
"folder": "Song",
"characteristic": "Standard",
"difficulty": "ExpertPlus",
"port": 17425,
"password": ""
}

+ 346
- 0
Server/package-lock.json View File

@ -0,0 +1,346 @@
{
"name": "mediocremapperserver",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ajv": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
"integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"requires": {
"tweetnacl": "^0.14.3"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"mime-db": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
},
"mime-types": {
"version": "2.1.21",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
"requires": {
"mime-db": "~1.37.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"psl": {
"version": "1.1.31",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
"integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"request": {
"version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.0",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.4.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sshpk": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz",
"integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
"requires": {
"psl": "^1.1.24",
"punycode": "^1.4.1"
},
"dependencies": {
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
}
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"requires": {
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
}
}
}

+ 15
- 0
Server/package.json View File

@ -0,0 +1,15 @@
{
"name": "mediocremapperserver",
"version": "1.0.0",
"description": "Server for Mediocre Mapper Multi-Mapper",
"main": "server.js",
"scripts": {
"test": "start",
"start": "node server.js"
},
"author": "squeaksies",
"license": "ISC",
"dependencies": {
"request": "^2.88.0"
}
}

+ 730
- 0
Server/server.js View File

@ -0,0 +1,730 @@
"use strict";
const fs = require("fs");
var clients = [];
var net = require('net');
try{
var request = require('request');
}
catch(err){
console.log(err);
console.log('Go read the damn readme. Run "npm install" or install the request module in some other way.');
process.exit()
}
var https = require('https');
var config = {};
var diff;
var fileSize;
var diffIndex;
var charIndex;
var diffFound = false;
var charFound = false;
var changesMade = false;
var downloadURL = "";
console.log("\n\n\n");
// Loading up json files and such that you need
try{
config = require('./config.json');
}
catch(err){
}
process.argv.shift();
process.argv.shift();
var type = "";
while(process.argv.length > 0)
{
var input = process.argv.shift()
if (input.startsWith("--"))
{
type = input.replace('--','');
}
else
{
if (type == "")
{
console.log('you fucked up');
process.exit()
}
else
{
config[type] = input;
}
}
}
// Check and display relevant info
try{
if(config.folder !== "" && config.folder !== undefined){
console.log(' Song folder: ' + config.folder);
}
else{
console.log('\nError: Fill in folder field');
process.exit()
}
if(config.difficulty !== "" && config.characteristic !== undefined){
console.log(' Song characteristic: ' + config.characteristic);
}
else{
console.log(' Song characteristic: Standard');
}
if(config.difficulty !== "" && config.difficulty !== undefined){
console.log(' Song difficulty: ' + config.difficulty);
}
else{
console.log('\nError: Fill in difficulty field');
process.exit()
}
if(config.port !== "" && config.port !== undefined){
console.log(' Port: ' + config.port);
}
else{
console.log('\nError: Fill in port field');
process.exit()
}
if(config.password != "" && config.password !== undefined){
console.log(' Password: ' + config.password);
}
else{
config.password = "";
console.log(' Password: no password being used');
}
if(config.download !== "" && config.download !== undefined){
console.log(' Song download: ' + config.download);
downloadURL = config.download;
}
}
catch(err){
console.log('config file is not formatted correctly');
process.exit()
}
var data = fs.readFileSync('./Songs/' + config.folder + '/info.dat');
// Loading stuff
try{
var info = loadJSON('./Songs/' + config.folder + '/info.dat');
}
catch(err){
console.log('Could not find file: Songs/' + config.folder + '/info.dat');
process.exit()
}
if (!config.characteristic)
{
config.characteristic = "Standard";
}
for (var i = 0; i < info._difficultyBeatmapSets.length; i++)
{
if (info._difficultyBeatmapSets[i]._beatmapCharacteristicName == config.characteristic)
{
for(var j = 0; j < info._difficultyBeatmapSets[i]._difficultyBeatmaps.length; j++)
{
if (info._difficultyBeatmapSets[i]._difficultyBeatmaps[j]._difficulty == config.difficulty)
{
diff = loadJSON('./Songs/' + config.folder + '/' + info._difficultyBeatmapSets[i]._difficultyBeatmaps[j]._beatmapFilename);
try{
fileSize = fs.statSync('./Songs/' + config.folder + '/' + info._songFilename);
}catch(err)
{
//console.log('\nFailed to find '+info.difficultyLevels[i].audioPath+'. now using fileSize argument');
if(!config.hasOwnProperty('fileSize'))
{
//console.log('could not use fileSize argument, finding from download URL');
request({
url: downloadURL,
method: "HEAD"
}, function(err, response, body) {
var downloadResponse = response.headers;
if (downloadResponse.hasOwnProperty('content-length'))
{
fileSize = downloadResponse['content-length'];
}
else
{
console.log('Unable to get file size. Please specify fileSize in config.json or as an argument');
process.exit();
}
});
}
fileSize = config['content-length'];
}
diffIndex = j;
charIndex = i;
diffFound = true;
break;
}
}
}
}
if (diff === null)
{
console.log('No proper difficulty found.');
process.exit();
}
if (diffFound == false)
{
console.log('Difficulty/Characteristic not found');
process.exit();
}
setInterval(saveFile, 300000);//save every 5 minutes
// if(config.download === "")
// {
// config.download = 'none';
// }
var server = net.createServer(function(socket){
socket.name = "wait";
socket.send = false;
//add new client to the list
clients.push(socket);
// Handle incoming messages from clients.
socket.on('data', function (data) {
if(socket.name == "wait")
{
if (config.password !== "" && data.toString().includes(";|;"))
{
console.log("attempted connection: " + data.toString().split(";|;")[1] + "\nPassword: " +data.toString().split(";|;")[0]);
if (config.password == data.toString().split(";|;")[0])
{
for (var i = 0; i < clients.length; i++)
{
if (clients[i].name == data.toString().split(";|;")[1] || "wait" == data.toString().split(";|;")[1])
{
if (socket.address == clients[i].address)
{
console.log('duplicate from same client. Destroying old client.')
clients[i].destroy();
clients.splice(i, 1);
}
else
{
socket.write('dc:duplicate name');
clients.splice(clients.indexOf(socket), 1);
socket.destroy();
return;
}
}
}
socket.name = data.toString().split(";|;")[1];
// Send a nice welcome message and announce
socket.write(config.folder.split('/').pop()+"::"+diffIndex*100+ charIndex+";;;"); //0
socket.write(JSON.stringify(info)+";;;"); //1
socket.write(info._difficultyBeatmapSets[charIndex]._difficultyBeatmaps[diffIndex]._beatmapFilename+";;;"); //2
socket.write(JSON.stringify(diff)+";;;");//3
socket.write(info._songFilename+";;;"+fileSize+";;;");//4 & 5
socket.write(downloadURL +";;;" );//6
socket.send = true;
}
else
{
socket.write('dc:wrong password');
clients.splice(clients.indexOf(socket), 1);
socket.destroy();
return;
}
}
else if (config.password === "")
{
if(data.toString().includes(";|;"))
{
data = data.toString().split(";|;")[1];
}
console.log("attempted connection: " + data);
for (var i = 0; i < clients.length; i++)
{
if (clients[i].name == data || "wait" == data)
{
socket.write('dc:duplicate name');
clients.splice(clients.indexOf(socket), 1);
socket.destroy();
return;
}
}
socket.name = data.toString();
// Send a nice welcome message and announce
socket.write(config.folder.split('/').pop()+"::"+diffIndex*100+ charIndex+";;;"); //0
socket.write(JSON.stringify(info)+";;;"); //1
socket.write(info._difficultyBeatmapSets[charIndex]._difficultyBeatmaps[diffIndex]._beatmapFilename+";;;"); //2
socket.write(JSON.stringify(diff)+";;;");//3
socket.write(info._songFilename+";;;"+fileSize+";;;");//4 & 5
socket.write(downloadURL +";;;" );//6
socket.send = true;
}
else if (config.password !== "" && !data.toString().includes(";|;"))
{
socket.write('dc:password required');
clients.splice(clients.indexOf(socket), 1);
socket.destroy();
return;
}
else
{
socket.write('dc:uhhh something else');
clients.splice(clients.indexOf(socket), 1);
socket.destroy();
return;
}
console.log(socket.name + " joined the session.");
broadcast("System:,:sy:,:"+ socket.name + " joined the session.;:;");
}
else
{
broadcast(data.toString(), socket);
var commands = data.toString().split(";:;");
for (var i = 0; i < commands.length; i++)
{
readMessage(commands[i]);
}
}
});
// Remove the client from the list when it leaves
socket.on('end', function () {
try
{
clients.splice(clients.indexOf(socket), 1);
console.log(socket.name + " left the session.");
broadcast("System:,:sy:,:"+ socket.name + " left the session.;:;");
broadcast(socket.name + ":,:rc;:;");
}
catch(err)
{
console.log(err);
}
});
socket.on('error',function () {
try
{
clients.splice(clients.indexOf(socket), 1);
broadcast("System:,:sy:,:"+ socket.name + " left the session.;:;");
broadcast(socket.name + ":,:rc;:;");
}
catch(err)
{
console.log(err);
}
});
// Send a message to all clients
function broadcast(message, sender) {
clients.forEach(function (client) {
// Don't want to send it to sender
if (client === sender)
{
return;
}
if (client.send == false)
{
return;
}
client.write(message);
});
}
});
//Get IP address
console.log('\n\n\n');
if (downloadURL === undefined || downloadURL === "")
{
var req = request.post('https://catbox.moe/user/api.php', function (err, resp, body) {
if (err) {
console.log(err);
} else {
// var downloadInfo = JSON.parse(body)
downloadURL = body;
console.log('Download URL generated: ' + body);
https.get('https://ifconfig.co/ip', function(res){
res.setEncoding('utf8');
res.on('data', function(chunk){
server.listen(config.port, '0.0.0.0');
process.stdout.write("Server Started on IP address: ")
console.log(chunk);
});
res.on('error', function(err) {
server.listen(config.port, '0.0.0.0');
process.stdout.write("Server Started");
});
});
}
});
var form = req.form();
form.append('reqtype', 'fileupload');
form.append('fileToUpload', fs.createReadStream('./Songs/'+config.folder+'/'+info._songFilename));
}else
{
https.get('https://ifconfig.co/ip', function(res){
res.setEncoding('utf8');
res.on('data', function(chunk){
server.listen(config.port, '0.0.0.0');
process.stdout.write("Server Started on IP address: ")
console.log(chunk);
});
res.on('error', function(err) {
server.listen(config.port, '0.0.0.0');
process.stdout.write("Server Started");
});
});
}
function readMessage(message)
{
changesMade = true;
var type = message.split(":,:")[1];
switch(type)
{
case 'an':
try{
addNote(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
case 'rn':
try{
removeNote(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
case 'ae':
try{
addEvent(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
case 're':
try{
removeEvent(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
case 'aw':
try{
addWall(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
case 'rw':
try{
removeWall(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
case 'ab':
try{
addBookmark(message.split(':,:')[2].split("|||"));
}
catch(err){
}
break;
case 'rb':
try{
removeBookmark(message.split(':,:')[2].split(" "));
}
catch(err){
}
break;
default:
//console.log(message);
break;
}
}
function addNote(data)
{
var note = stringToNote(data)
if (!hasNull(note))
{
diff._notes.push(note);
}
}
function removeNote(data)
{
for (var j = diff._notes.length -1 ; j >= 0; j--)
{
if (sameNote(diff._notes[j],stringToNote(data)))
{
diff._notes.splice(j,1);
break;
}
}
}
function stringToNote(data)
{
return {_time: parseFloat(data[0]),
_lineIndex: parseInt(data[1]),
_lineLayer: parseInt(data[2]),
_type: parseInt(data[3]),
_cutDirection: parseInt(data[4])};
}
function sameNote(note1,note2)
{
if (Math.abs(note1._time - note2._time) > 0.001)
return false;
if (note1._lineIndex != note2._lineIndex)
return false;
if (note1._lineLayer != note2._lineLayer)
return false;
if (note1._type != note2._type)
return false;
if (note1._cutDirection != note2._cutDirection)
return false;
return true;
}
function addEvent(data)
{
if (data.length == 3)
{
var event = stringToEvent(data)
if (!hasNull(event))
{
diff._events.push(event);
}
}
else
{
var event = stringToEvent(data)
if (!hasNull(event))
{
diff._BPMChanges.push(stringToBPM(data));
}
}
}
function removeEvent(data)
{
if (data.length == 3)
{
for (var j = diff._events.length -1 ; j >= 0; j--)
{
if (sameEvent(diff._events[j],stringToEvent(data)))
{
diff._events.splice(j,1);
break;
}
}
}
else
{
for (var j = diff._BPMChanges.length -1 ; j >= 0; j--)
{
if (sameBPM(stringToBPM(data), diff._BPMChanges[j]))
{
diff._BPMChanges.splice(j,1);
}
}
}
}
function sameEvent(event1,event2)
{
if (Math.abs(event1._time - event2._time) > 0.001)
return false;
if (event1._type != event2._type)
return false;
if (event1._value != event2._value)
return false;
return true;
}
function sameBPM(event1,event2)
{
if (Math.abs(event1._BPM - event2._BPM) > 0.02)
return false;
if (Math.abs(event1._time - event2._time) > 0.001)
return false;
return true;
}
function stringToEvent(data)
{
return {
_time: parseFloat(data[0]),
_type: parseInt(data[1]),
_value: parseInt(data[2])
};
}
function stringToBPM(data)
{
return {
_BPM:parseFloat(data[0]),
_time:parseFloat(data[1]),
_beatsPerBar:parseFloat(data[2]),
_metronomeOffset:parseFloat(data[3])}
}
function addWall(data)
{
var wall = stringToWall(data)
if (!hasNull(wall))
{
diff._obstacles.push(wall);
}
}
function removeWall(data)
{
for (var j = diff._obstacles.length -1 ; j >= 0; j--)
{
if (sameWall(diff._obstacles[j],stringToWall(data)))
{
diff._obstacles.splice(j,1);
break;
}
}
}
function stringToWall(data)
{
return {_time: parseFloat(data[0]),
_lineIndex: parseInt(data[1]),
_type: parseInt(data[2]),
_duration: parseFloat(data[3]),
_width: parseInt(data[4])
};
}
function sameWall(wall1,wall2)
{
if (Math.abs(wall1._time - wall2._time) > 0.001)
return false;
if (wall1._lineIndex != wall2._lineIndex)
return false;
if (wall1._type != wall2._type)
return false;
if (Math.abs(wall1._duration - wall2._duration) > 0.001)
return false;
if (wall1._width != wall2._width)
return false;
return true;
}
function addBookmark(data)
{
diff._bookmarks.push(stringToBookmark(data));
}
function removeBookmark(data)
{
for (var j = diff._bookmarks.length - 1; j >= 0; j--)
{
if (Math.abs(diff._bookmarks[j]._time - stringToBookmark(data)._time) < 0.001)
{
diff._bookmarks.splice(j,1);
break;
}
}
}
function stringToBookmark(data)
{
return {
_time: parseFloat(data[0]),
_name: data[1]
};
}
function saveFile()
{
if (changesMade)
{
fs.writeFile('./Songs/' + config.folder + '/' + info._difficultyBeatmapSets[charIndex]._difficultyBeatmaps[diffIndex]._beatmapFilename, JSON.stringify(diff), function(err) {
if (err)
console.log(err);
else
{
console.log ("file successfully saved.\nNote Count: "+ diff._notes.length+"\nEvent Count: "+diff._events.length);
}
});
}
changesMade = false;
}
// server.getConnections(function(err,count){
// console.log(count);
// });
function disconnectSoon(socket)
{
if(socket.name == "wait" || socket.name.length > 30)
{
clients.splice(clients.indexOf(socket), 1);
socket.destroy();
return;
}
}
function hasNull(object)
{
for(var key in object) {
if (object[key] === null)
{
try
{
console.log("null object detected at :" + object._time);
}
catch(err)
{
console.log("null object detected")
}
return true;
}
}
}
function loadJSON(file) {
var data = fs.readFileSync(file);
return JSON.parse(data);
}

+ 249
- 0
Source/MediocreMapAssistant2/BPFileIO.cpp View File

@ -2,6 +2,13 @@
#include "BPFileIO.h"
#include "ModuleManager.h"
#include "FileHelper.h"
#include "ImageUtils.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
bool UBPFileIO::VerifyOrCreateDirectory(const FString & TestDir)
{
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
@ -31,6 +38,217 @@ bool UBPFileIO::VerifyDirectory(const FString & TestDir)
return true;
}
uint8 getMpegVersion(uint8 num) {
/*
00 - MPEG Version 2.5 (unofficial)
01 - reserved
10 - MPEG Version 2 (ISO/IEC 13818-3)
11 - MPEG Version 1 (ISO/IEC 11172-3)
*/
if ((num & 0x03) == 0x03) {
return 1;
}
if ((num & 0x02) == 0x02) {
return 2;
}
return 0;
}
uint8 getMpegLayer(uint8 num) {
/**
00 - reserved
01 - Layer III
10 - Layer II
11 - Layer I
*/
if ((num & 0x03) == 0x03) {
return 1;
}
if ((num & 0x02) == 0x02) {
return 2;
}
if ((num & 0x01) == 0x01) {
return 3;
}
return 0;
}
int BITRATES[3][5][16] = { {},
{
{},
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{},
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
}
};
int SAMPLE_RATES[3][4] = {
{0, 0, 0, 0},
{44100, 48000, 32000, 0},
{22050, 24000, 16000, 0}
};
int SAMPLES_PER_FRAME[3][4] = {
{0, 0, 0, 0},
{0, 384, 1152, 1152},
{0, 384, 1152, 576}
};
int getMpegFrameLength(bool has_padding, int sample_rate, uint8 layer, int bitrate, int num_samples) {
uint8 padding = has_padding ? 1 : 0;
if (sample_rate == 0) {
return 0;
}
if (layer == 1) {
return ((int) (12.0 * bitrate / sample_rate + padding)) * 4;
}
return ((int) (num_samples * (bitrate / 8) / sample_rate)) + padding;
}
uint8 getMpegChannels(uint8 num) {
/*
00 - Stereo
01 - Joint stereo (Stereo)
10 - Dual channel (2 mono channels)
11 - Single channel (Mono)
*/
if ((num & 0x03) == 0x03) {
return 1;
}
return 2;
}
int CheckFrameInfo(TArray<uint8> FileData, int i, int depth) {
bool isMP3 = (FileData[i] << 8 | (FileData[i + 1] & 0xF0)) == 0xFFF0;
if (!isMP3) return -1;
bool padding = (FileData[i + 2] & 0x02) >> 1 == 0x01;
uint8 mpegVersion = getMpegVersion(FileData[i + 1] >> 3);
uint8 mpegLayer = getMpegLayer(FileData[i + 1] >> 1);
int bitRate = BITRATES[mpegVersion][mpegLayer][(FileData[i + 2] >> 4) & 0x0F] * 1000;
int sampleRate = SAMPLE_RATES[mpegVersion][(FileData[i + 2] >> 2) & 0x03];
int sampleCount = SAMPLES_PER_FRAME[mpegVersion][mpegLayer];
uint8 channels = getMpegChannels(FileData[i + 3] >> 6);
int frameLength = getMpegFrameLength(padding, sampleRate, mpegLayer, bitRate, sampleCount);
if (frameLength == 0) return -1;
UE_LOG(LogTemp, Error, TEXT("Possible mp3 V: %d, L: %d, P: %d, Bitrate: %d, Channels: %d, Sample rate: %d, Sample count: %d, Frame len: %d"), mpegVersion, mpegLayer, padding, bitRate, channels, sampleRate, sampleCount, frameLength);
if (depth < 2) {
int otherBitrate = CheckFrameInfo(FileData, i + frameLength, depth + 1);
if (otherBitrate != bitRate) {
return -1;
}
}
return bitRate;
}
FString UBPFileIO::CheckAudioFormatMatches(const FString & TestPath)
{
FString unknownProblem = "Error loading song file";
TArray<uint8> FileData;
if (!FFileHelper::LoadFileToArray(FileData, *TestPath))
{
UE_LOG(LogTemp, Error, TEXT("Failed to load file"));
return unknownProblem;
}
if (FileData.Num() < 1024) {
return unknownProblem;
}
for (int i = 0; i < 1024; i++) {
if ((FileData[i] << 8 | (FileData[i+1] & 0xF0)) == 0xFFF0) {
if (CheckFrameInfo(FileData, i, 0) != -1) {
if (TestPath.EndsWith(".ogg")) {
return "That looks like an MP3, you can't just rename files to change their type.\nGo get audacity and convert it properly.";
}
return "That looks like an MP3, but it needs to be an ogg.\nPlease use audacity to convert it.";
}
return unknownProblem;
}
}
return unknownProblem;
}
FString UBPFileIO::CheckImageFormatMatches(const FString & TestPath)
{
FString unknownProblem = "Something's weird with your cover image, and i don't know what it is.";
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
TArray<uint8> FileData;
if (!FFileHelper::LoadFileToArray(FileData, *TestPath))
{
UE_LOG(LogTemp, Error, TEXT("Failed to load file"));
return unknownProblem;
}
EImageFormat fileFormat = ImageWrapperModule.DetectImageFormat(FileData.GetData(), FileData.Num());
if ((TestPath.EndsWith(".png") && fileFormat == EImageFormat::PNG) ||
((TestPath.EndsWith(".jpg") || TestPath.EndsWith(".jpeg")) && (fileFormat == EImageFormat::JPEG || fileFormat == EImageFormat::GrayscaleJPEG)))
{
return unknownProblem;
} else if (!TestPath.EndsWith(".png") && !TestPath.EndsWith(".jpg") && !TestPath.EndsWith(".jpeg")) {
return "Your cover image needs to be a jpg or png and end with .jpg, .jpeg or .png";
}
FString fileFormatStr;
switch (fileFormat) {
case EImageFormat::BMP:
fileFormatStr = "BMP";
break;
case EImageFormat::PNG:
fileFormatStr = "PNG";
break;
case EImageFormat::JPEG:
case EImageFormat::GrayscaleJPEG:
fileFormatStr = "JPG";
break;
case EImageFormat::ICO:
fileFormatStr = "ICO";
break;
case EImageFormat::EXR:
fileFormatStr = "EXR";
break;
case EImageFormat::ICNS:
fileFormatStr = "ICNS";
break;
case EImageFormat::Invalid:
default:
fileFormatStr = "Unknown";
}
return "Your cover image appears to be a " + fileFormatStr + " file, but the extension does not match";
}
TArray<FString> UBPFileIO::FindAllDirectories(const FString & TestDir)
{
TArray<FString> result;
@ -118,4 +336,35 @@ int UBPFileIO::getFileSize(const FString & File)
int UBPFileIO::getTimestamp(const FString & File)
{
return FPlatformFileManager::Get().GetPlatformFile().GetTimeStamp(*File).ToUnixTimestamp();
}
bool UBPFileIO::SaveStringTextToFile(
FString SaveDirectory,
FString JoyfulFileName,
FString SaveText,
bool AllowOverWriting
) {
if (!FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*SaveDirectory))
{
//Could not make the specified directory
return false;
//~~~~~~~~~~~~~~~~~~~~~~
}
//get complete file path
SaveDirectory += "\\";
SaveDirectory += JoyfulFileName;
//No over-writing?
if (!AllowOverWriting)
{
//Check if file exists already
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*SaveDirectory))
{
//no overwriting
return false;
}
}
return FFileHelper::SaveStringToFile(SaveText, *SaveDirectory, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
}

+ 13
- 1
Source/MediocreMapAssistant2/BPFileIO.h View File

@ -23,6 +23,12 @@ class MEDIOCREMAPASSISTANT2_API UBPFileIO : public UBlueprintFunctionLibrary
UFUNCTION(BlueprintCallable, Category = "File IO")
static bool VerifyDirectory(const FString& TestDir);
UFUNCTION(BlueprintCallable, Category = "File IO")
static FString CheckAudioFormatMatches(const FString& TestPath);
UFUNCTION(BlueprintCallable, Category = "File IO")
static FString CheckImageFormatMatches(const FString& TestPath);
UFUNCTION(BlueprintCallable, Category = "File IO")
static TArray<FString> FindAllDirectories(const FString& TestDir);
@ -49,5 +55,11 @@ class MEDIOCREMAPASSISTANT2_API UBPFileIO : public UBlueprintFunctionLibrary
UFUNCTION(BlueprintCallable, Category = "File IO")
static int getTimestamp(const FString& File);
UFUNCTION(BlueprintCallable, Category = "File IO")
static bool SaveStringTextToFile(
FString SaveDirectory,
FString JoyfulFileName,
FString SaveText,
bool AllowOverWriting);
};

+ 1
- 1
Source/MediocreMapAssistant2/RenderWaveform.cpp View File

@ -220,7 +220,7 @@ void URenderWaveform::BP_RenderWaveform(USoundWave* InSoundWaveRef, UProceduralM
for (size_t j = 0; j < 64; ++j){
float height;
if (valid) height = results[j * 8.f] / 50000.f;
if (valid && results.Num() > (j * 8.f)) height = results[j * 8.f] / 50000.f;
else height = 0;
Vertices[To1D(i, j, SizeX)] = FVector(i, j, height);


Loading…
Cancel
Save