5 Commits

Author SHA1 Message Date
67380670d7 - fix: item path in unpack 2025-05-11 22:41:15 +03:00
39308fc316 - add: Settings, start 2025-05-10 19:19:30 +03:00
17cc6f0928 - add: icons 2025-05-07 18:43:26 +03:00
64a1a6a9ee - add: automatic version change 2025-05-07 14:55:00 +03:00
f3f8df2d3e - tweak: change manifest upload path 2025-05-07 14:17:18 +03:00
20 changed files with 281 additions and 12 deletions

View File

@@ -1,9 +1,9 @@
name: Publish launcher manifest and files
on:
workflow_dispatch:
# schedule:
# - cron: '0 10 * * *'
push:
tags:
- 'v*'
jobs:
build:
@@ -20,6 +20,8 @@ jobs:
dotnet-version: 9.0.x
- name: Install dependencies
run: dotnet restore
- name: Set version
run: echo "${{ github.ref }}" > ./Nebula.Launcher/Version.txt
- name: Build whole project
run: dotnet build --configuration Release
- name: Package launcher files
@@ -31,4 +33,4 @@ jobs:
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./release/
server-dir: ./release/
server-dir: ./

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -58,6 +58,13 @@
<Copy SourceFiles="..\Nebula.Runner\bin\$(Configuration)\$(TargetFramework)\Nebula.Runner.runtimeconfig.json" DestinationFolder="$(PublishDir)"/>
</Target>
<ItemGroup>
<None Remove="Version.txt" />
<EmbeddedResource Include="Version.txt">
<LogicalName>Nebula.Launcher.Version.txt</LogicalName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj"/>
<ProjectReference Include="..\Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>

BIN
Nebula.Launcher/Version.txt Normal file

Binary file not shown.

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
@@ -24,11 +25,13 @@ public partial class MainViewModel : ViewModelBase
new ListItemTemplate(typeof(AccountInfoViewModel), "user", "Account", null),
new ListItemTemplate(typeof(ServerListViewModel), "file", "Servers", false),
new ListItemTemplate(typeof(ServerListViewModel), "star", "Favorites", true),
new ListItemTemplate(typeof(ContentBrowserViewModel), "folder", "Content", null)
new ListItemTemplate(typeof(ContentBrowserViewModel), "folder", "Content", null),
new ListItemTemplate(typeof(ConfigurationViewModel), "settings", "Settings", null)
];
private readonly List<PopupViewModelBase> _viewQueue = new();
[ObservableProperty] private string _versionInfo = "dev";
[ObservableProperty] private ViewModelBase _currentPage;
[ObservableProperty] private PopupViewModelBase? _currentPopup;
[ObservableProperty] private string _currentTitle = "Default";
@@ -57,6 +60,13 @@ public partial class MainViewModel : ViewModelBase
protected override void Initialise()
{
_logger = DebugService.GetLogger(this);
using var stream = typeof(MainViewModel).Assembly
.GetManifestResourceStream("Nebula.Launcher.Version.txt")!;
using var streamReader = new StreamReader(stream);
VersionInfo = streamReader.ReadLine() ?? "dev";
InitialiseInDesignMode();
PopupMessageService.OnPopupRequired += OnPopupRequired;
@@ -145,7 +155,7 @@ public partial class MainViewModel : ViewModelBase
public void OpenLink()
{
Helper.OpenBrowser("https://cinka.ru/nebula-launcher/");
Helper.OpenBrowser("https://durenko.tatar/nebula");
}
private void OnPopupRequired(object viewModelBase)

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.Views.Config;
using Nebula.Launcher.Views.Pages;
using Nebula.Shared.Services;
using BindingFlags = System.Reflection.BindingFlags;
namespace Nebula.Launcher.ViewModels.Pages;
[ViewModelRegister(typeof(ConfigurationView))]
[ConstructGenerator]
public partial class ConfigurationViewModel : ViewModelBase, IConfigContext
{
public ObservableCollection<ViewModelBase> ConfigurationVerbose { get; } = new();
[GenerateProperty] private ConfigurationService ConfigurationService { get; } = default!;
[GenerateProperty] private IServiceProvider ServiceProvider { get; } = default!;
public void AddConfiguration<T,T1>(ConVar<T> convar) where T1: ViewModelBase, IConfigurationVerbose<T>
{
var configurationVerbose = ServiceProvider.GetService<T1>()!;
configurationVerbose.Context = new ConfigContext<T>(convar, this);
configurationVerbose.InitializeConfig();
ConfigurationVerbose.Add(configurationVerbose);
}
public void InvokeUpdateConfiguration()
{
foreach (var verbose in ConfigurationVerbose)
{
if(verbose is not IUpdateInvoker invoker) continue;
invoker.UpdateConfiguration();
}
}
protected override void InitialiseInDesignMode()
{
AddConfiguration<string, StringConfigurationViewModel>(LauncherConVar.ILSpyUrl);
}
protected override void Initialise()
{
InitialiseInDesignMode();
}
public void SetValue<T>(ConVar<T> conVar, T value)
{
ConfigurationService.SetConfigValue(conVar, value);
}
public T? GetValue<T>(ConVar<T> convar)
{
return ConfigurationService.GetConfigValue<T>(convar);
}
}
public interface IConfigContext
{
public void SetValue<T>(ConVar<T> conVar, T value);
public T? GetValue<T>(ConVar<T> convar);
}
public class ConfigContext<T> : IConfigContext
{
public ConfigContext(ConVar<T> conVar, IConfigContext parent)
{
ConVar = conVar;
Parent = parent;
}
public ConVar<T> ConVar { get; }
public IConfigContext Parent { get; }
public T? GetValue()
{
return GetValue(ConVar);
}
public void SetValue(T? value)
{
SetValue(ConVar!, value);
}
public void SetValue<T1>(ConVar<T1> conVar, T1 value)
{
Parent.SetValue(conVar, value);
}
public T1? GetValue<T1>(ConVar<T1> convar)
{
return Parent.GetValue(convar);
}
}
public interface IConfigurationVerbose<T>
{
public ConfigContext<T> Context { get; set; }
public void InitializeConfig();
}
public interface IUpdateInvoker
{
public void UpdateConfiguration();
}
[ViewModelRegister(typeof(StringConfigurationView))]
public partial class StringConfigurationViewModel : ViewModelBase , IConfigurationVerbose<string>, IUpdateInvoker
{
[ObservableProperty] private string _configText = string.Empty;
[ObservableProperty] private string? _configName = string.Empty;
private string _oldText = string.Empty;
public ConfigContext<string> Context { get; set; }
public void InitializeConfig()
{
ConfigName = Context.ConVar.Name;
_oldText = Context.GetValue() ?? string.Empty;
ConfigText = _oldText;
}
public void UpdateConfiguration()
{
if (_oldText == ConfigText) return;
Context.SetValue(ConfigText);
}
protected override void InitialiseInDesignMode()
{
}
protected override void Initialise()
{
}
}

View File

@@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:pages="clr-namespace:Nebula.Launcher.ViewModels.Pages"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.Config.StringConfigurationView"
x:DataType="pages:StringConfigurationViewModel">
<Design.DataContext>
<pages:StringConfigurationViewModel />
</Design.DataContext>
<StackPanel Orientation="Horizontal" Spacing="5" Margin="5">
<TextBlock VerticalAlignment="Center" Text="{Binding ConfigName}"/>
<TextBlock VerticalAlignment="Center" Text=":"/>
<TextBox Text="{Binding ConfigText}"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.Views.Config;
public partial class StringConfigurationView : UserControl
{
public StringConfigurationView()
{
InitializeComponent();
}
public StringConfigurationView(StringConfigurationViewModel viewModel)
: this()
{
DataContext = viewModel;
}
}

View File

@@ -117,10 +117,10 @@
Foreground="#777777"
HorizontalAlignment="Left"
VerticalAlignment="Center">
https://cinka.ru/nebula-launcher/
https://durenko.tatar/nebula/
</TextBlock>
</Button>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center">v0.08-a</TextBlock>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Text="{Binding VersionInfo}"/>
</Panel>
</Label>
</Border>

View File

@@ -4,7 +4,7 @@
ExtendClientAreaTitleBarHeightHint="-1"
ExtendClientAreaToDecorationsHint="True"
Height="500"
Icon="/Assets/avalonia-logo.ico"
Icon="/Assets/nebula.ico"
MinHeight="500"
MinWidth="800"
SystemDecorations="BorderOnly"

View File

@@ -0,0 +1,40 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:pages="clr-namespace:Nebula.Launcher.ViewModels.Pages"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.Pages.ConfigurationView"
x:DataType="pages:ConfigurationViewModel">
<Design.DataContext>
<pages:ConfigurationViewModel />
</Design.DataContext>
<Panel>
<Border
VerticalAlignment="Top"
Margin="5" Padding="5,2,5,2">
<Border.Background>
<LinearGradientBrush EndPoint="50%,100%" StartPoint="50%,0%">
<GradientStop Color="#222222" Offset="0.0" />
<GradientStop Color="#292222" Offset="1.0" />
</LinearGradientBrush>
</Border.Background>
<ScrollViewer >
<StackPanel>
<ItemsControl
ItemsSource="{Binding ConfigurationVerbose}"
Padding="0" />
</StackPanel>
</ScrollViewer>
</Border>
<Button
VerticalAlignment="Bottom"
HorizontalAlignment="Stretch"
Padding="5"
Margin="5"
Command="{Binding InvokeUpdateConfiguration}"
>Save
</Button>
</Panel>
</UserControl>

View File

@@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.Views.Pages;
public partial class ConfigurationView : UserControl
{
public ConfigurationView()
{
InitializeComponent();
}
public ConfigurationView(ConfigurationViewModel viewModel)
: this()
{
DataContext = viewModel;
}
}

View File

@@ -33,7 +33,10 @@ public static class ConVarBuilder
[ServiceRegister]
public class ConfigurationService
{
public delegate void OnConfigurationChangedDelegate<in T>(T value);
public IReadWriteFileApi ConfigurationApi { get; init; }
private readonly ILogger _logger;
public ConfigurationService(FileService fileService, DebugService debugService)
@@ -42,6 +45,11 @@ public class ConfigurationService
ConfigurationApi = fileService.CreateFileApi("config");
}
private void SubscribeVarChanged<T>(ConVar<T> convar, OnConfigurationChangedDelegate<T> @delegate)
{
}
public T? GetConfigValue<T>(ConVar<T> conVar)
{

View File

@@ -83,7 +83,7 @@ public partial class ContentService
if (hashApi.TryOpen(item, out var stream))
{
_logger.Log($"Unpack {item.Hash} to: {item.Path}");
otherApi.Save(item.Hash, stream);
otherApi.Save(item.Path, stream);
stream.Close();
}
else

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -87,7 +87,7 @@ public static class ConfigurationStandalone
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}

View File

@@ -9,6 +9,7 @@
<PublishSingleFile>true</PublishSingleFile>
<!--<PublishTrimmed>true</PublishTrimmed>-->
<SelfContained>true</SelfContained>
<ApplicationIcon>Assets/nebula.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>

View File

@@ -33,6 +33,7 @@ public static class RestStandalone
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return defaultValue;
}
}

View File

@@ -5,7 +5,7 @@ namespace Nebula.UpdateResolver;
public static class UpdateConVars
{
public static readonly ConVar<string> UpdateCacheUrl =
ConVarBuilder.Build<string>("update.url","https://cinka.ru/nebula-launcher/files/publish/release");
ConVarBuilder.Build<string>("update.url","https://durenko.tatar/nebula/manifest/");
public static readonly ConVar<LauncherManifest> CurrentLauncherManifest =
ConVarBuilder.Build<LauncherManifest>("update.manifest");
}

View File

@@ -11,6 +11,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpResponseMessage_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4cfeb8b377bc81e1fbb5f7d7a02492cb6ac23e88c8c9d7155944f0716f3d4b_003FHttpResponseMessage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIDisposable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003F98_003Fd1b23281_003FIDisposable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJsonSerializer_002ERead_002EString_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F27c4858128168eda568c1334d70d5241efb9461e2a3209258a04deee5d9c367_003FJsonSerializer_002ERead_002EString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObservableCollection_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3e2c48e6b3ec8b39cf721287f93972c7f3df25d306753bcc539eaad73126c68_003FObservableCollection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParallel_002EForEachAsync_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc1d1ed6be2d5d4de542b4af5b36e82f6d1d1a389a35a4e4f9748d137d1c651_003FParallel_002EForEachAsync_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceCollectionContainerBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa8ceca48b7b645dd875a40ee6d28725416d08_003F1b_003F6cd78dc8_003FServiceCollectionContainerBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceProviderServiceExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4f1fdec7cbfe4433a7ec3a6d1bd0e54210118_003F04_003Fe2f5322d_003FServiceProviderServiceExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>