Files
Barış Keser 83be058d23 feat: Athena v2 — Discord auth, AthenaCore mod, cross-platform launcher, CI
Backend (PHP+SQLite): Discord OAuth2 + JWT, coklu hesap (1 Discord = 3-5),
tek-kullanimlik join token (/api/join/prepare+verify), custom skin yukleme/sunma,
hesap/Discord/HWID ban, admin paneli kartlari; deterministik offline UUID (PHP<->C#
paritesi dogrulandi). 25 entegrasyon testi gecti.

AthenaCore (Forge 1.12.2): tek client+server mod — join token dogrulama + kick,
grace-gate, custom skin uygulama. Sunucu offline-mode kalir (UUID/dunya verisi korunur).

Masaustu: WPF -> Avalonia (Windows+Linux), Discord giris/hesap/skin/oyna; WMI/DPAPI
yerine cross-platform (machine-id HWID, AES-GCM secret store). Derlenir + calisir.

CI: athenacore-mod.yml (JDK8), desktop-release.yml (Win+Linux self-contained).
Deploy: deploy.sh (.env uretimi, php-gd, nginx Authorization gecisi) + HANDOFF.md.
Android: backend hazir; entegrasyon spec'i ANDROID_INTEGRATION.md.
2026-06-13 23:45:21 +03:00

100 lines
3.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using OfflineMinecraftLauncher.Services;
namespace OfflineMinecraftLauncher.Views;
public partial class ModsView : UserControl
{
private readonly LauncherCore _core;
private readonly LocalSettings _settings;
private bool _loaded;
private HashSet<string> _enabled = new();
public ModsView(LauncherCore core, LocalSettings settings)
{
_core = core;
_settings = settings;
InitializeComponent();
}
public async Task RefreshAsync()
{
if (_loaded) return;
_loaded = true;
_enabled = LoadEnabled();
bool firstRun = string.IsNullOrWhiteSpace(_settings.EnabledOptionalMods);
var mods = await _core.FetchOptionalModsAsync();
List.Children.Clear();
if (mods.Count == 0)
{
Empty.Text = "İsteğe bağlı mod yok.";
return;
}
Empty.IsVisible = false;
foreach (var m in mods)
{
bool on = _enabled.Contains(m.Id) || (firstRun && m.Default);
if (on) _enabled.Add(m.Id);
var cb = new CheckBox { IsChecked = on, Content = BuildContent(m) };
string id = m.Id;
cb.IsCheckedChanged += (_, _) =>
{
if (cb.IsChecked == true) _enabled.Add(id); else _enabled.Remove(id);
Persist();
};
List.Children.Add(new Border { Classes = { "card" }, Child = cb });
}
Persist();
}
private static Control BuildContent(OptionalMod m)
{
var sp = new StackPanel { Spacing = 4, Margin = new Thickness(6, 0, 0, 0) };
sp.Children.Add(new TextBlock
{
Text = string.IsNullOrWhiteSpace(m.Name) ? m.Id : m.Name,
FontWeight = FontWeight.SemiBold
});
if (!string.IsNullOrWhiteSpace(m.Description))
sp.Children.Add(new TextBlock { Text = m.Description, Classes = { "muted" }, TextWrapping = TextWrapping.Wrap });
sp.Children.Add(new TextBlock { Text = FormatSize(m.Size), Classes = { "muted" }, FontSize = 11 });
return sp;
}
private static string FormatSize(long bytes)
{
if (bytes >= 1024 * 1024) return $"{bytes / 1024.0 / 1024.0:0.0} MB";
if (bytes >= 1024) return $"{bytes / 1024.0:0} KB";
return $"{bytes} B";
}
private HashSet<string> LoadEnabled()
{
try
{
if (!string.IsNullOrWhiteSpace(_settings.EnabledOptionalMods))
{
var arr = JsonSerializer.Deserialize<List<string>>(_settings.EnabledOptionalMods);
if (arr != null) return new HashSet<string>(arr);
}
}
catch { }
return new HashSet<string>();
}
private void Persist()
{
_settings.EnabledOptionalMods = JsonSerializer.Serialize(_enabled.ToList());
_settings.Save();
}
}