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

144 lines
4.9 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;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace OfflineMinecraftLauncher.Pages;
public partial class ModsPage : UserControl
{
private readonly LauncherCore _core;
private List<OptionalMod> _all = new();
private readonly HashSet<string> _enabled = new(StringComparer.OrdinalIgnoreCase);
private bool _loaded;
private bool _loading;
public ModsPage(LauncherCore core)
{
_core = core;
InitializeComponent();
LoadEnabled();
}
private void LoadEnabled()
{
_enabled.Clear();
try
{
var saved = Properties.Settings.Default.EnabledOptionalMods;
if (!string.IsNullOrWhiteSpace(saved))
{
var arr = JsonSerializer.Deserialize<List<string>>(saved);
if (arr != null) foreach (var id in arr) if (!string.IsNullOrWhiteSpace(id)) _enabled.Add(id);
}
}
catch { }
}
public async void Reload()
{
if (_loaded) { Render(); return; }
if (_loading) return; // eşzamanlı tekrar girişi engelle
_loading = true;
try
{
_all = await _core.FetchOptionalModsAsync() ?? new List<OptionalMod>();
// İlk açılışta hiç seçim kaydı yoksa "default" modları işaretle
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.EnabledOptionalMods))
{
foreach (var m in _all) if (m.Default) _enabled.Add(m.Id);
SaveEnabled();
}
_loaded = true;
}
catch { _all = new List<OptionalMod>(); }
finally { _loading = false; }
Render();
}
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e) => Render();
private void Render()
{
ModsList.Children.Clear();
string q = (SearchBox.Text ?? "").Trim().ToLowerInvariant();
var items = _all.Where(m => string.IsNullOrEmpty(q)
|| (m.Name ?? "").ToLowerInvariant().Contains(q)
|| (m.Id ?? "").ToLowerInvariant().Contains(q)).ToList();
EmptyLabel.Text = _all.Count == 0 ? "Şu anda kullanılabilir opsiyonel mod yok." : "Aramanızla eşleşen mod yok.";
EmptyLabel.Visibility = items.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
foreach (var mod in items)
ModsList.Children.Add(BuildRow(mod));
UpdateSummary();
}
private Border BuildRow(OptionalMod mod)
{
var card = new Border
{
Style = (Style)FindResource("Card"),
Padding = new Thickness(16, 12, 16, 12),
Margin = new Thickness(0, 0, 0, 8)
};
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
var info = new StackPanel();
info.Children.Add(new TextBlock { Text = string.IsNullOrWhiteSpace(mod.Name) ? mod.Id : mod.Name, FontSize = 15, FontWeight = FontWeights.SemiBold });
if (!string.IsNullOrWhiteSpace(mod.Description))
info.Children.Add(new TextBlock { Text = mod.Description, FontSize = 12, Foreground = (Brush)FindResource("TextMutedBrush"), TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 3, 0, 0) });
info.Children.Add(new TextBlock { Text = SizeText(mod.Size), FontSize = 11, Foreground = (Brush)FindResource("TextMutedBrush"), Margin = new Thickness(0, 4, 0, 0) });
Grid.SetColumn(info, 0);
var toggle = new CheckBox
{
Style = (Style)FindResource("BrandToggle"),
VerticalAlignment = VerticalAlignment.Center,
IsChecked = _enabled.Contains(mod.Id)
};
toggle.Click += (s, e) =>
{
if (toggle.IsChecked == true) _enabled.Add(mod.Id); else _enabled.Remove(mod.Id);
SaveEnabled();
UpdateSummary();
};
Grid.SetColumn(toggle, 1);
grid.Children.Add(info);
grid.Children.Add(toggle);
card.Child = grid;
return card;
}
private void UpdateSummary()
{
long bytes = _all.Where(m => _enabled.Contains(m.Id)).Sum(m => m.Size);
int n = _all.Count(m => _enabled.Contains(m.Id));
SummaryText.Text = $"{n} mod seçili · ~{SizeText(bytes)} indirilecek";
}
private static string SizeText(long bytes)
{
if (bytes <= 0) return "0 MB";
double mb = bytes / 1048576.0;
return mb >= 1 ? $"{mb:0.0} MB" : $"{bytes / 1024.0:0} KB";
}
private void SaveEnabled()
{
try
{
Properties.Settings.Default.EnabledOptionalMods = JsonSerializer.Serialize(_enabled.ToList());
Properties.Settings.Default.Save();
}
catch { }
}
}