Compare commits

...

13 Commits
master ... gui

Author SHA1 Message Date
AveryMadness
d6658d8cdc ignore session 2024-02-04 15:33:44 -07:00
u4pak
5f3cee0a99 lol 2024-02-04 14:31:28 -08:00
AveryMadness
10733c1d80 asdasd 2024-02-04 13:39:01 -07:00
AveryMadness
102c918d63 asdfhjklbadfsbnhkasdfghkasdfghjkasdfghjkasdfkasdfghjksdfk 2024-02-04 13:37:26 -07:00
McMistrzYT
f042c4adb3 MAWHAHAHA 2024-02-04 21:28:46 +01:00
u4pak
aa104a9660 discord rpc 2024-01-28 12:07:22 -08:00
AveryMadness
1d4667d6e0 forgot 2024-01-23 20:36:13 -07:00
AveryMadness
104738edee necksa bowled 2024-01-23 20:21:27 -07:00
AveryMadness
2b13cd746e icon, and launch stuff 2024-01-23 17:45:56 -07:00
AveryMadness
53cd921941 forgot title 2024-01-23 14:29:05 -07:00
AveryMadness
675455a990 buncha shit 2024-01-23 14:16:16 -07:00
u4pak
1d6231789c fix user data encoding & decoding 2024-01-22 17:52:20 -08:00
McMistrzYT
41b09152d3 gui push 2024-01-23 00:54:56 +01:00
24 changed files with 904 additions and 178 deletions

View File

@ -1,9 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.33927.289
# Visual Studio Version 17
VisualStudioVersion = 17.8.34219.65
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Partypacker", "Partypacker\Partypacker.csproj", "{FE06B383-0C7A-4A35-B208-66133110BB32}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Partypacker", "Partypacker\Partypacker.csproj", "{BCB1E673-FE06-4360-895D-07FE682C720D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,15 +11,15 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FE06B383-0C7A-4A35-B208-66133110BB32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE06B383-0C7A-4A35-B208-66133110BB32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE06B383-0C7A-4A35-B208-66133110BB32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE06B383-0C7A-4A35-B208-66133110BB32}.Release|Any CPU.Build.0 = Release|Any CPU
{BCB1E673-FE06-4360-895D-07FE682C720D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCB1E673-FE06-4360-895D-07FE682C720D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCB1E673-FE06-4360-895D-07FE682C720D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCB1E673-FE06-4360-895D-07FE682C720D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {16591BB0-2EFE-43B1-917D-7CD574AB32C8}
SolutionGuid = {F12064C4-E05B-464A-BF11-E0E20C7D2B3F}
EndGlobalSection
EndGlobal

15
Partypacker/App.xaml Normal file
View File

@ -0,0 +1,15 @@
<Application x:Class="Partypacker.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Partypacker"
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/AdonisUI;component/ColorSchemes/Dark.xaml"/>
<ResourceDictionary Source="pack://application:,,,/AdonisUI.ClassicTheme;component/Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

14
Partypacker/App.xaml.cs Normal file
View File

@ -0,0 +1,14 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace Partypacker
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

21
Partypacker/Classes.cs Normal file
View File

@ -0,0 +1,21 @@
namespace Partypacker
{
public enum UserRole
{
User = 100,
VerifiedUser = 200,
TrackVerifier = 250,
Moderator = 300,
Administrator = 400
}
public class UserDetailObject
{
public string ID;
public string Username;
public string GlobalName;
public string Avatar;
public bool IsAdmin;
public UserRole Role;
}
}

View File

@ -47,4 +47,4 @@ namespace Partypacker.Core
public RegistryValueKind RegistryValueKind { get; set; }
}
}
}

View File

@ -17,4 +17,4 @@ namespace Partypacker.Core
.ForEach(x => registryKey
.SetValue(x.Name, x.Value, x.RegistryValueKind));
}
}
}

View File

@ -1,18 +1,34 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Partypacker.Core
{
internal class Server
internal class PartypackServer
{
public static string BaseURL =
#if DEBUG
MainWindow.settings.GetValue("Launcher", "apiurl") ?? "https://partypack.mcthe.dev";
#else
MainWindow.settings.GetValue("Launcher", "apiurl") ?? "https://partypack.mcthe.dev";
#endif
public static string DashboardURL =
#if DEBUG
MainWindow.settings.GetValue("Launcher", "apiurl") ?? "https://partypack.mcthe.dev";
#else
MainWindow.settings.GetValue("Launcher", "dashurl") ?? "https://partypack.mcthe.dev";
#endif
public static KeyValuePair<bool, string> GET(string URL = "/")
{
if (URL.StartsWith("/"))
URL = "https://example.com" + URL;
URL = BaseURL + URL;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "GET";
@ -31,14 +47,14 @@ namespace Partypacker.Core
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
MessageBox.Show(ex.Message);
return new KeyValuePair<bool, string>(false, string.Empty);
}
}
public static KeyValuePair<bool, string> POST(string URL = "/", string Body = "")
{
if (URL.StartsWith("/"))
URL = "https://example.com" + URL;
URL = BaseURL + URL;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "POST";
@ -66,5 +82,5 @@ namespace Partypacker.Core
}
}
}
}

View File

@ -87,4 +87,4 @@ namespace Partypacker
INTERNET_OPTION_SETTINGS_CHANGED = 39
}
}
}
}

275
Partypacker/INIFile.cs Normal file
View File

@ -0,0 +1,275 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
namespace Partypacker
{
public class INIFile
{
private string _File;
/// <summary>
/// Call the constructor creates a new object of the INIFile class to work with INI files.
/// </summary>
/// <param name="file">Name of INI file, which you want to access.</param>
/// <param name="createFile">Specifies whether the INI file should be created if it does not exist.</param>
public INIFile(string file, bool createFile = false)
{
if (createFile == true && File.Exists(file) == false)
{
FileInfo fileInfo = new FileInfo(file);
FileStream fileStream = fileInfo.Create();
fileStream.Close();
}
_File = file;
}
#region Public Methods
/// <summary>
/// Removes all comments and empty lines from a complete section and returns the sections.
/// This method is not case-sensitive.
/// The return value does not contain any spaces at the beginning or at the end of a line.
/// </summary>
/// <param name="section">Name of the requested section.</param>
/// <param name="includeComments">Specifies whether comments should also be returned.</param>
/// <returns>Returns the whole section.</returns>
public List<string> GetSection(string section, bool includeComments = false)
{
section = CheckSection(section);
List<string> completeSection = new List<string>();
bool sectionStart = false;
string[] fileArray = File.ReadAllLines(_File);
foreach (var item in fileArray)
{
if (item.Length <= 0) continue;
// Beginning of section.
if (item.Replace(" ", "").ToLower() == section)
{
sectionStart = true;
}
// Beginning of next section.
if (sectionStart == true && item.Replace(" ", "").ToLower() != section && item.Replace(" ", "").Substring(0, 1) == "[" && item.Replace(" ", "").Substring(item.Length - 1, 1) == "]")
{
break;
}
if (sectionStart == true)
{
// Add the entry to the List<string> completeSection, if it is not a comment or an empty entry.
if (includeComments == false
&& item.Replace(" ", "").Substring(0, 1) != ";" && !string.IsNullOrWhiteSpace(item))
{
completeSection.Add(ReplaceSpacesAtStartAndEnd(item));
}
if (includeComments == true && !string.IsNullOrWhiteSpace(item))
{
completeSection.Add(ReplaceSpacesAtStartAndEnd(item));
}
}
}
return completeSection;
}
/// <summary>
/// The method returns a value for the associated key.
/// This method is not case-sensitive.
/// </summary>
/// <param name="section">Name of the requested section.</param>
/// <param name="key">Name of the requested key.</param>
/// <param name="convertValueToLower">If "true" is passed, the value will be returned in lowercase letters.</param>
/// <returns>Returns the value for the specified key in the specified section, if available, otherwise null.</returns>
public string GetValue(string section, string key, bool convertValueToLower = false)
{
section = CheckSection(section);
key = key.ToLower();
List<string> completeSection = GetSection(section);
foreach (var item in completeSection)
{
// Continue if entry is no key.
if (!item.Contains("=") && item.Contains("[") && item.Contains("]")) continue;
string[] keyAndValue = item.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
if (keyAndValue[0].ToLower() == key && keyAndValue.Count() > 1)
{
if (convertValueToLower == true)
{
keyAndValue[1] = keyAndValue[1].ToLower();
}
return keyAndValue[1];
}
}
return null;
}
/// <summary>
/// Set or add a value of the associated key in the specified section.
/// This method is not case-sensitive.
/// </summary>
/// <param name="section">Name of the section.</param>
/// <param name="key">Name of the key.</param>
/// <param name="value">Value to save.</param>
/// <param name="convertValueToLower">If "true" is passed, the value will be saved in lowercase letters.</param>
public void SetValue(string section, string key, string value, bool convertValueToLower = false)
{
section = CheckSection(section, false);
string sectionToLower = section.ToLower();
bool sectionFound = false;
List<string> iniFileContent = new List<string>();
string[] fileLines = File.ReadAllLines(_File);
// Creates a new INI file if none exists.
if (fileLines.Length <= 0)
{
iniFileContent = AddSection(iniFileContent, section, key, value, convertValueToLower);
WriteFile(iniFileContent);
return;
}
for (int i = 0; i < fileLines.Length; i++)
{
// Possibility 1: The desired section has not (yet) been found.
if (fileLines[i].Replace(" ", "").ToLower() != sectionToLower)
{
iniFileContent.Add(fileLines[i]);
// If a section does not exist, the section will be created.
if (i == fileLines.Length - 1 && fileLines[i].Replace(" ", "").ToLower() != sectionToLower && sectionFound == false)
{
iniFileContent.Add(null);
iniFileContent = AddSection(iniFileContent, section, key, value, convertValueToLower);
break;
}
continue;
}
// Possibility 2 -> Desired section was found.
sectionFound = true;
// Get the complete section in which the target key may be.
List<string> targetSection = GetSection(sectionToLower, true);
for (int x = 0; x < targetSection.Count; x++)
{
string[] targetKey = targetSection[x].Split(new string[] { "=" }, StringSplitOptions.None);
// When the target key is found.
if (targetKey[0].ToLower() == key.ToLower())
{
if (convertValueToLower == true)
{
iniFileContent.Add(key + "=" + value.ToLower());
}
else
{
iniFileContent.Add(key + "=" + value);
}
i = i + x;
break;
}
else
{
iniFileContent.Add(targetSection[x]);
// If the target key is not found, it will be created.
if (x == targetSection.Count - 1 && targetKey[0].ToLower() != key.ToLower())
{
if (convertValueToLower == true)
{
iniFileContent.Add(key + "=" + value.ToLower());
}
else
{
iniFileContent.Add(key + "=" + value);
}
i = i + x;
break;
}
}
}
}
WriteFile(iniFileContent);
}
#endregion
#region Private Methods
/// <summary>
/// Ensures that a section is always in the following format: [section].
/// </summary>
/// <param name="section">Section to be checked for correct format.</param>
/// <param name="convertToLower">Specifies whether the section should be vonverted in lower case letters.</param>
/// <returns>Returns section in this form: [section].</returns>
private string CheckSection(string section, bool convertToLower = true)
{
if (convertToLower == true)
{
section = section.ToLower();
}
if (!section.StartsWith("[") && !section.EndsWith("]"))
{
section = "[" + section + "]";
}
return section;
}
/// <summary>
/// Removes leading and trailing spaces from sections, keys and values.
/// </summary>
/// <param name="item">String to be trimmed.</param>
/// <returns>Returns the trimmed string.</returns>
private string ReplaceSpacesAtStartAndEnd(string item)
{
// If the string has a key and a value.
if (item.Contains("=") && !item.Contains("[") && !item.Contains("]"))
{
string[] keyAndValue = item.Split(new string[] { "=" }, StringSplitOptions.None);
return keyAndValue[0].Trim() + "=" + keyAndValue[1].Trim();
}
return item.Trim();
}
/// <summary>
/// Adds a new section with key value pair.
/// </summary>
/// <param name="iniFileContent">List iniFileContent from SetValue.</param>
/// <param name="section">Section to be created.</param>
/// <param name="key">Key to be added.</param>
/// <param name="value">Value to be added.</param>
/// <param name="convertValueToLower">Specifies whether the key and value should be saved in lower case letters.</param>
/// <returns>Returns the new created section with key value pair.</returns>
private List<string> AddSection(List<string> iniFileContent, string section, string key, string value, bool convertValueToLower)
{
if (convertValueToLower == true)
{
value = value.ToLower();
}
iniFileContent.Add(section);
iniFileContent.Add($"{key}={value}");
return iniFileContent;
}
private void WriteFile(List<string> content)
{
StreamWriter writer = new StreamWriter(_File);
foreach (var item in content)
{
writer.WriteLine(item);
}
writer.Close();
}
#endregion
}
}

BIN
Partypacker/Icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

View File

@ -0,0 +1,52 @@
<Window x:Class="Partypacker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Partypacker"
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
mc:Ignorable="d"
ResizeMode="CanMinimize"
Title="Partypack Launcher" Height="450" Width="800">
<Window.Style>
<Style TargetType="Window" BasedOn="{StaticResource {x:Type Window}}"/>
</Window.Style>
<Window.Resources>
<Style x:Key="NexaBoldFont">
<Setter Property="TextElement.FontFamily" Value="#NexaBold"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="250" />
<ColumnDefinition Width="10"/>
<ColumnDefinition />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<TextBox PreviewTextInput="TextBox_PreviewTextInput" Grid.Column="3" adonisExtensions:WatermarkExtension.Watermark="Proxy Port (default: 6969)" TextChanged="OnChangePort" Margin="10,10,260,0" Grid.RowSpan="2" Grid.Row="6" />
<Button x:Name="LaunchButton" Grid.Row="4" Grid.Column="3" Click="OnLaunch" IsEnabled="False" Margin="10,10,260,0" Grid.RowSpan="2">Launch</Button>
<Button x:Name="LoginButton" Grid.Row="2" Grid.Column="3" Click="OnLoginUsingDiscord" Margin="10,10,260,0" Grid.RowSpan="2">Log in using Discord</Button>
<Ellipse x:Name="PFPContainer" MouseLeftButtonDown="OnDashboard" RenderOptions.BitmapScalingMode="HighQuality" Margin="0,8,195,211" Grid.Column="1" Stretch="Uniform" Visibility="Hidden">
<Ellipse.Fill>
<ImageBrush x:Name="ProfilePictureImage" ImageSource="https://cdn.discordapp.com/avatars/419224403415662592/b9486673bd2a484314a1e718c9d9c57b.webp" Stretch="UniformToFill"/>
</Ellipse.Fill>
</Ellipse>
<TextBlock x:Name="UsernameTextBlock" HorizontalAlignment="Left" Margin="43,16,0,0" TextWrapping="Wrap" Text="AveryMadness (@averymadness)" VerticalAlignment="Top" Grid.Column="1" Visibility="Hidden"/>
<Image x:Name="Logo" Grid.Column="3" HorizontalAlignment="Left" Height="201" VerticalAlignment="Top" Width="200" Source="/Icon.png" Margin="26,41,0,0" Stretch="Uniform"/>
<TextBlock Grid.Column="3" HorizontalAlignment="Left" Margin="44,222,0,0" TextWrapping="Wrap" Text="Partypack" VerticalAlignment="Top" Height="67" Width="182" FontSize="36" Style="{DynamicResource NexaBoldFont}" Grid.RowSpan="2"/>
</Grid>
</Window>

View File

@ -0,0 +1,251 @@
using DiscordRPC;
using DiscordRPC.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Partypacker.Core;
using Partypacker.Net;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Transactions;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using WatsonWebserver;
namespace Partypacker
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
static Proxy Proxx;
static string DiscordAuthURL;
static int Port = 6969;
static string Token;
static UserDetailObject UserDetails;
static Server sv;
public static INIFile settings = new("settings.ini", true);
public MainWindow()
{
InitializeComponent();
InitializeRPC();
Application.Current.Exit += OnApplicationExit;
Process.GetCurrentProcess().Exited += MainWindow_Exited;
var DiscordURL = PartypackServer.GET("/api/discord/url");
if (!DiscordURL.Key)
{
MessageBox.Show("Failed to contact the Partypack API. (Is it running?)", "Partypack API Error", MessageBoxButton.OK, MessageBoxImage.Error);
Environment.Exit(-1);
return;
}
DiscordAuthURL = DiscordURL.Value;
if (!string.IsNullOrWhiteSpace(settings.GetValue("Launcher", "token")))
AutoLogin();
}
private void MainWindow_Exited(object? sender, EventArgs e)
{
Proxx?.StopProxy();
}
void OnApplicationExit(object sender, ExitEventArgs e) => Proxx?.StopProxy();
void OnChangePort(object sender, TextChangedEventArgs e) {
if (!int.TryParse(((TextBox)sender).Text, out int P))
return;
Port = P;
}
void OnDashboard(object sender, MouseButtonEventArgs e) => Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = PartypackServer.DashboardURL + "/profile"});
private void UpdateUserUI()
{
this.Dispatcher.Invoke(() =>
{
UsernameTextBlock.Text = string.IsNullOrWhiteSpace(UserDetails.GlobalName) ? UserDetails.Username : @$"{UserDetails.GlobalName} (@{UserDetails.Username})";
ProfilePictureImage.ImageSource = (ImageSource)new ImageSourceConverter().ConvertFromString(UserDetails.Avatar);
UsernameTextBlock.Visibility = Visibility.Visible;
PFPContainer.Visibility = Visibility.Visible;
});
}
void AutoLogin()
{
var B64Token = settings.GetValue("Launcher", "token");
var NonB64Bytes = Convert.FromHexString(B64Token);
var NonB64Str = Encoding.UTF8.GetString(NonB64Bytes);
Token = NonB64Str;
UserDetails = JsonConvert.DeserializeObject<UserDetailObject>(Encoding.UTF8.GetString(Convert.FromHexString(HttpUtility.UrlDecode(settings.GetValue("Launcher", "user")))));
UpdateUserUI();
Dispatcher.Invoke(() => LaunchButton.IsEnabled = true);
ConvertLoginToLogout();
}
private void ConvertLoginToLogout()
{
Dispatcher.Invoke(() =>
{
LoginButton.Content = "Log Out";
LoginButton.Click -= OnLoginUsingDiscord;
LoginButton.Click += OnLogout;
});
}
private void OnLogout(object sender, RoutedEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
UsernameTextBlock.Visibility = Visibility.Hidden;
PFPContainer.Visibility = Visibility.Hidden;
UserDetails = null;
Token = "";
settings.SetValue("Launcher", "user", "");
settings.SetValue("Launcher", "token", "");
LoginButton.Content = "Log in using Discord";
LoginButton.Click += OnLoginUsingDiscord;
LoginButton.Click -= OnLogout;
});
}
private async Task DefaultRoute(HttpContext ctx)
{
string _Token = ctx.Request.Query.Elements["token"];
string _UserDetails = ctx.Request.Query.Elements["user"];
if (_Token == null || _UserDetails == null)
{
await ctx.Response.Send($"Invalid request. ({_Token}, {_UserDetails})");
return;
}
Token = _Token;
UserDetails = JsonConvert.DeserializeObject<UserDetailObject>(Encoding.UTF8.GetString(Convert.FromHexString(HttpUtility.UrlDecode(_UserDetails))));
settings.SetValue("Launcher", "user", HttpUtility.UrlDecode(_UserDetails));
settings.SetValue("Launcher", "token", Convert.ToHexString(Encoding.UTF8.GetBytes(Token)));
UpdateUserUI();
Dispatcher.Invoke(() =>
{
LaunchButton.IsEnabled = true;
});
ConvertLoginToLogout();
await ctx.Response.Send("All done! You can close this tab now.");
sv.Stop();
sv = null;
}
void OnLoginUsingDiscord(object sender, RoutedEventArgs e)
{
Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = $"{DiscordAuthURL}&state={HttpUtility.UrlEncode(Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new
{
Client = "PartypackerDesktop"
}))))}"});
if (sv == null)
{
sv = new Server("127.0.0.1", 14968, false, DefaultRoute);
sv.Start();
}
}
bool WaitingForGameToOpen = true;
System.Timers.Timer GameCheckTimer;
private void CheckForProcessAndClose(object sender, ElapsedEventArgs e)
{
Process[] processes = Process.GetProcessesByName("FortniteLauncher");
if (WaitingForGameToOpen)
{
if (processes.Length > 0)
WaitingForGameToOpen = false;
return;
}
if (processes.Length > 0)
return;
Dispatcher.Invoke(() =>
{
LaunchButton.IsEnabled = true;
});
WaitingForGameToOpen = true;
GameCheckTimer.Stop();
Proxx?.StopProxy();
}
async void OnLaunch(object sender, RoutedEventArgs e)
{
Proxx = new Proxy(Port);
Proxx.Token = Token;
Proxx.StartProxy();
using (Process p = Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = "com.epicgames.launcher://apps/fn%3A4fe75bbc5a674f4f9b356b5c90567da5%3AFortnite?action=launch&silent=true" }))
{
LaunchButton.IsEnabled = false;
GameCheckTimer = new System.Timers.Timer();
GameCheckTimer.Interval = 5000;
GameCheckTimer.Elapsed += CheckForProcessAndClose;
GameCheckTimer.Start();
}
}
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
public DiscordRpcClient client;
void InitializeRPC()
{
client = new DiscordRpcClient("1198605718169858088");
client.Logger = new ConsoleLogger() { Level = LogLevel.Warning };
client.OnReady += (sender, e) =>
{
Console.WriteLine("Received Ready from user {0}", e.User.Username);
};
client.OnPresenceUpdate += (sender, e) =>
{
Console.WriteLine("Received Update! {0}", e.Presence);
};
client.Initialize();
client.SetPresence(new RichPresence()
{
Details = "Modding Fortnite Festival",
State = "Loading Custom Tracks",
Timestamps = new DiscordRPC.Timestamps()
{
Start = DateTime.UtcNow,
},
Assets = new Assets()
{
LargeImageKey = "logo",
LargeImageText = "Partypacker - Alpha",
//SmallImageKey = "image_small"
},
Buttons = new DiscordRPC.Button[]
{
new DiscordRPC.Button() {Label = "Check out Partypack!", Url = "https://partypack.mcthe.dev/"}
}
});
}
}
}

BIN
Partypacker/Nexa Bold.otf Normal file

Binary file not shown.

BIN
Partypacker/PartyPacker.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -1,27 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ApplicationIcon>icon.ico</ApplicationIcon>
<PackageIcon>icon.ico</PackageIcon>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ApplicationIcon>PartyPacker.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCMakeCert" Version="2.0.9" />
<PackageReference Include="FiddlerCore.Trial" Version="5.0.0" />
<PackageReference Include="Pastel" Version="5.0.0" />
<PackageReference Include="System.ComponentModel.Composition" Version="8.0.0" />
<PackageReference Include="Telerik.NetworkConnections" Version="0.2.0" />
<None Remove="favicon.png" />
<None Remove="Icon.png" />
<None Remove="Nexa Bold.otf" />
</ItemGroup>
<ItemGroup>
<None Include="icon.ico">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<Content Include="PartyPacker.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AdonisUI" Version="1.17.1" />
<PackageReference Include="AdonisUI.ClassicTheme" Version="1.17.1" />
<PackageReference Include="BCMakeCert" Version="2.0.9" />
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageReference Include="FiddlerCore.Trial" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pastel" Version="5.0.0" />
<PackageReference Include="Telerik.NetworkConnections" Version="0.2.0" />
<PackageReference Include="Watson" Version="5.1.3" />
</ItemGroup>
<ItemGroup>
<Resource Include="Icon.png" />
<Resource Include="Nexa Bold.otf" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -1,112 +0,0 @@
using Partypacker.Net;
using Pastel;
using System.Diagnostics;
using System.Drawing;
using static Partypacker.Win32;
namespace Partypacker
{
public static class Program
{
private static SetConsoleCtrlEventHandler CtrlHandler;
private static Proxy Proxx;
private static void Main(string[] args)
{
ushort? port = 6969;
Console.SetWindowSize(75, 20);
Console.CursorVisible = false;
Console.WriteLine(@"
_____ _ _
| __ \ | | | |
| |__) |_ _ _ __| |_ _ _ _ __ __ _ ___| | _____ _ __
| ___/ _` | '__| __| | | | '_ \ / _` |/ __| |/ / _ \ '__|
| | | (_| | | | |_| |_| | |_) | (_| | (__| < __/ |
|_| \__,_|_| \__|\__, | .__/ \__,_|\___|_|\_\___|_|
__/ | |
|___/|_| ".Pastel(Color.IndianRed));
Console.WriteLine("-----------------------------------------------------------".Pastel(Color.CadetBlue));
Console.WriteLine("Welcome to Partypacker - Select an option below:");
ConsoleKeyInfo ReceivedKeyInput;
int SelectedOptionIndex = 0;
bool Running = true;
SelectableOption[] Options = new SelectableOption[]
{
new SelectableOption("Launch Fortnite", () =>
Run(port, () => Process.Start(new ProcessStartInfo { FileName = "com.epicgames.launcher://apps/fn%3A4fe75bbc5a674f4f9b356b5c90567da5%3AFortnite?action=launch&silent=true", UseShellExecute = true }))),
new SelectableOption("Open Dashboard", () => Process.Start(new ProcessStartInfo { FileName = "https://partypack.mcthe.dev", UseShellExecute = true }))
};
try
{
while (Running)
{
(int left, int top) = Console.GetCursorPosition();
for (int i = 0; i < Options.Length; i++)
{
bool Selected = SelectedOptionIndex == i;
SelectableOption Option = Options[i];
Console.WriteLine($"{(Selected ? ">".Pastel(Color.LimeGreen) : " ")} {Option.Name.Pastel(Selected ? Color.DarkGreen : Color.Gray)}");
}
ReceivedKeyInput = Console.ReadKey();
switch (ReceivedKeyInput.Key)
{
case ConsoleKey.UpArrow:
SelectedOptionIndex--;
break;
case ConsoleKey.DownArrow:
SelectedOptionIndex++;
break;
case ConsoleKey.Enter:
Options[SelectedOptionIndex].OnPress.Invoke();
break;
}
Console.SetCursorPosition(left, top);
SelectedOptionIndex = Math.Clamp(SelectedOptionIndex, 0, Options.Length);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Debug.Fail(e.StackTrace);
}
finally
{
OnApplicationExit();
}
}
private static void Run(ushort? port, Action Finish)
{
CtrlHandler = CleanUp;
SetConsoleCtrlHandler(CtrlHandler, true);
Proxx = port switch
{
null => new Proxy(),
_ => new Proxy((ushort)port)
};
Proxx.StartProxy();
Finish.Invoke();
}
public static void OnApplicationExit()
{
CleanUp(CtrlType.CTRL_C_EVENT);
}
private static bool CleanUp(CtrlType ctrlType)
{
Proxx?.StopProxy();
return true;
}
}
}

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Partypacker.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Partypacker.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -47,4 +47,4 @@ namespace Partypacker.Net
rsaCng.Clear();
}
}
}
}

View File

@ -20,6 +20,7 @@ namespace Partypacker.Net
// https://github.com/PsychoPast/LawinServer/blob/master/LawinServer/Proxy/Proxy.cs
// without it, fiddler-less proxying would have never been achieved
public string Token = "";
private const string Proxy_Server = "ProxyServer";
private const string Proxy_Enable = "ProxyEnable";
private readonly AppRegistry appRegistry;
@ -31,14 +32,14 @@ namespace Partypacker.Net
public Proxy() : this(9999) { } //default port
public Proxy(ushort port)
public Proxy(int port)
{
appRegistry = new AppRegistry();
GetDefaultProxySettingsValue();
ConfigureFiddlerSettings(out bool fiddlerCertRegKeysExist);
startupSettings = new FiddlerCoreStartupSettingsBuilder()
.ListenOnPort(port)
.ListenOnPort((ushort)port)
.RegisterAsSystemProxy()
.DecryptSSL()
.OptimizeThreadPool()
@ -129,11 +130,18 @@ namespace Partypacker.Net
#region EVENT_HANDLERS
private void OnBeforeRequest(Session oSession)
{
if (oSession.PathAndQuery.Contains("/content/api/pages/fortnite-game/spark-tracks")
string BaseURL =
#if DEBUG
MainWindow.settings.GetValue("Launcher", "apiurl") ?? "https://partypack.mcthe.dev";
#else
MainWindow.settings.GetValue("Launcher", "apiurl") ?? "https://partypack.mcthe.dev";
#endif
if (oSession.PathAndQuery.Contains("/content/api/pages/fortnite-game")
|| oSession.HostnameIs("cdn.qstv.on.epicgames.com")
|| oSession.HostnameIs("cdn-0001.qstv.on.epicgames.com")
|| oSession.PathAndQuery.Contains("/master.blurl")
|| oSession.PathAndQuery.Contains("/main.blurl")
|| oSession.fullUrl.StartsWith(BaseURL)
)
{
if (oSession.HTTPMethodIs("CONNECT"))
@ -142,12 +150,7 @@ namespace Partypacker.Net
return;
}
string BaseURL =
#if DEBUG
"http://localhost:6677";
#else
"https://api.partypack.mcthe.dev";
#endif
oSession.RequestHeaders.Add("X-Partypack-Token", Token);
if (oSession.PathAndQuery.Contains("/master.blurl")
|| oSession.PathAndQuery.Contains("/main.blurl"))
@ -155,8 +158,12 @@ namespace Partypacker.Net
else
oSession.fullUrl = BaseURL + oSession.PathAndQuery;
}
else
{
oSession.Ignore();
}
}
#endregion
#endregion
#region CLEANUP
private bool ResetProxySettings()
@ -187,4 +194,4 @@ namespace Partypacker.Net
}
#endregion
}
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Partypacker
{
public class SelectableOption
{
public string Name;
public Action OnPress;
public SelectableOption(string name, Action onPress)
{
Name = name;
OnPress = onPress;
}
}
}

6
Partypacker/global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"version": "7.0.405",
"rollForward": "latestFeature"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB