1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-14 05:24:07 +08:00

Compare commits

..

32 Commits

1433 changed files with 117776 additions and 100662 deletions
+19 -24
View File
@@ -1,24 +1,19 @@
# Autodetect text files and ensure that we normalise their
# line endings to lf internally. When checked out they may
# use different line endings.
* text=auto
# Check out with crlf (Windows) line endings
*.sln text eol=crlf
*.csproj text eol=crlf
*.cs text diff=csharp eol=crlf
*.resx text eol=crlf
*.vsixmanifest text eol=crlf
packages.config text eol=crlf
App.config text eol=crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.snippet text eol=crlf
*.manifest text eol=crlf
# Check out with lf (UNIX) line endings
*.sh text eol=lf
.gitignore text eol=lf
.gitattributes text eol=lf
*.md text eol=lf
.travis.yml text eol=lf
# This won't normalise line endings, but it will ensure that merge drivers use CRLF
* -text eol=crlf
# Currently in-use binary file extensions
*.blend binary
*.bmp binary
*.dll binary
*.exe binary
*.icns binary
*.ico binary
*.jpg binary
*.osz2 binary
*.pdn binary
*.psd binary
*.PSD binary
*.tga binary
*.ttf binary
*.wav binary
*.xnb binary
+259 -259
View File
@@ -1,259 +1,259 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
bin/[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
Staging/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
bin/[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
Staging/
@@ -1,22 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (catch)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>
@@ -1,22 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (mania)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>
@@ -1,22 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (osu!)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>
@@ -1,22 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (taiko)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>
@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests (net471)" type="DotNetProject" factoryName=".NET Project" singleton="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Tests/osu.Game.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<method />
</configuration>
</component>
@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests (netcoreapp2.1)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Tests/osu.Game.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
<method />
</configuration>
</component>
@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (net471)" type="DotNetProject" factoryName=".NET Project" singleton="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net471/osu!.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<method />
</configuration>
</component>
@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (netcoreapp2.1)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs />
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
<method />
</configuration>
</component>
+1 -1
View File
@@ -1,2 +1,2 @@
language: csharp
language: csharp
solution: osu.sln
+19 -66
View File
@@ -1,113 +1,66 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "VisualTests (Debug, net471)",
"configurations": [{
"name": "osu! VisualTests (Debug)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe",
"args": [
"--tests"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"preLaunchTask": "Build (Debug)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, net471)",
"name": "osu! VisualTests (Release)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Game.Tests/bin/Release/net471/osu.Game.Tests.exe",
"program": "${workspaceRoot}/osu.Desktop/bin/Release/osu!.exe",
"args": [
"--tests"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Debug, net471)",
"name": "osu! (Debug)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/net471/osu!.exe",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"preLaunchTask": "Build (Debug)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Release, net471)",
"name": "osu! (Release)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Release/net471/osu!.exe",
"program": "${workspaceRoot}/osu.Desktop/bin/Release/osu!.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release, dotnet)",
"env": {},
"console": "internalConsole"
}
]
}
+3
View File
@@ -0,0 +1,3 @@
// Place your settings in this file to overwrite default and user settings.
{
}
+25 -72
View File
@@ -2,117 +2,70 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build (Debug, msbuild)",
"tasks": [{
"label": "Build (Debug)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/p:DebugType=portable",
"/m",
"/verbosity:m"
"/v:m"
],
"group": "build",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, msbuild)",
"label": "Build (Release)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:Configuration=Release",
"/p:TargetFramework=net471",
"/p:DebugType=portable",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
"/v:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build osu! (Debug, dotnet)",
"label": "Clean (Debug)",
"type": "shell",
"command": "dotnet",
"command": "msbuild",
"args": [
"build",
"--no-restore",
"osu.Desktop",
"/p:TargetFramework=netcoreapp2.1",
"/p:DebugType=portable",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
"/t:Clean",
"/v:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build osu! (Release, dotnet)",
"label": "Clean (Release)",
"type": "shell",
"command": "dotnet",
"command": "msbuild",
"args": [
"build",
"--no-restore",
"osu.Desktop",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/p:DebugType=portable",
"/m",
"/verbosity:m"
"/t:Clean",
"/v:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build tests (Debug, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Tests",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
"label": "Clean All",
"dependsOn": [
"Clean (Debug)",
"Clean (Release)"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build tests (Release, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Tests",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Restore (net471)",
"type": "shell",
"command": "nuget",
"args": [
"restore"
],
"problemMatcher": []
},
{
"label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
"restore"
],
"problemMatcher": []
}
]
}
+1 -1
View File
@@ -8,7 +8,7 @@ This is still heavily under development and is not intended for end-user use. Th
# Requirements
- A desktop platform that can compile .NET 4.7.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
# Getting Started
- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)
+80
View File
@@ -0,0 +1,80 @@
using osu.Framework.Graphics;
using OpenTK;
using Symcol.Core.Graphics.Containers;
namespace Symcol.Core.GameObjects
{
public class SymcolHitbox : SymcolContainer
{
/// <summary>
/// whether we want to do hit detection
/// </summary>
public int Team { get; set; }
/// <summary>
/// whether we want to do hit detection
/// </summary>
public bool HitDetection { get; set; } = true;
/// <summary>
/// the shape of this object (used for hit detection)
/// </summary>
public Shape Shape { get; }
public SymcolHitbox(Vector2 size, Shape shape = Shape.Circle)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Shape = shape;
Size = size;
if (Shape == Shape.Circle)
Child = new SymcolContainer
{
RelativeSizeAxes = Axes.Both,
CornerRadius = Width / 2
};
else if (Shape == Shape.Rectangle)
Child = new SymcolContainer
{
RelativeSizeAxes = Axes.Both
};
}
public bool HitDetect(SymcolHitbox hitbox1, SymcolHitbox hitbox2)
{
if (hitbox1.HitDetection && hitbox2.HitDetection && hitbox1.Team != hitbox2.Team)
{
if (hitbox1.Shape == Shape.Circle && hitbox2.Shape == Shape.Circle)
{
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
return true;
}
else if (hitbox1.Shape == Shape.Circle && hitbox2.Shape == Shape.Rectangle || hitbox1.Shape == Shape.Rectangle && hitbox2.Shape == Shape.Circle)
{
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
return true;
}
else if (hitbox1.Shape == Shape.Rectangle && hitbox2.Shape == Shape.Rectangle)
{
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
return true;
}
else if (hitbox1.Shape == Shape.Complex || hitbox2.Shape == Shape.Complex)
foreach (SymcolContainer child1 in hitbox1.Children)
foreach (SymcolContainer child2 in hitbox2.Children)
if (child1.ScreenSpaceDrawQuad.AABB.IntersectsWith(child2.ScreenSpaceDrawQuad.AABB))
return true;
}
return false;
}
}
public enum Shape
{
Circle,
Rectangle,
Complex
}
}
@@ -0,0 +1,12 @@
using osu.Framework.Graphics.Containers;
namespace Symcol.Core.Graphics.Containers
{
/// <summary>
/// Will support base eden game functions (if we come up with any)
/// </summary>
public class SymcolClickableContainer : ClickableContainer
{
}
}
@@ -0,0 +1,12 @@
using osu.Framework.Graphics.Containers;
namespace Symcol.Core.Graphics.Containers
{
/// <summary>
/// Will support base eden game functions (if we come up with any)
/// </summary>
public class SymcolContainer : Container
{
}
}
@@ -0,0 +1,58 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using System;
namespace Symcol.Core.Graphics.Containers
{
public class SymcolDialContainer : CircularContainer
{
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
private Vector2 mousePosition;
private float lastAngle;
private float currentRotation;
public float RotationAbsolute;
private int completeTick;
private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360));
private bool rotationTransferred;
protected override bool OnMouseMove(InputState state)
{
mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
return base.OnMouseMove(state);
}
protected override void Update()
{
base.Update();
var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
if (!rotationTransferred)
{
currentRotation = Rotation * 2;
rotationTransferred = true;
}
if (thisAngle - lastAngle > 180)
lastAngle += 360;
else if (lastAngle - thisAngle > 180)
lastAngle -= 360;
currentRotation += thisAngle - lastAngle;
RotationAbsolute += Math.Abs(thisAngle - lastAngle);
lastAngle = thisAngle;
foreach(Drawable drawable in Children)
drawable.RotateTo(currentRotation / 2, 200, Easing.OutExpo);
}
}
}
@@ -0,0 +1,43 @@
using osu.Framework.Input;
using OpenTK;
using OpenTK.Input;
namespace Symcol.Core.Graphics.Containers
{
public class SymcolDragContainer : SymcolContainer
{
protected override bool OnDragStart(InputState state) => true;
public bool AllowLeftClickDrag { get; set; } = true;
private bool drag;
private Vector2 startPosition;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
startPosition = Position;
if (args.Button == MouseButton.Left && AllowLeftClickDrag || args.Button == MouseButton.Right)
drag = true;
return base.OnMouseDown(state, args);
}
protected override bool OnDrag(InputState state)
{
if (drag)
Position = startPosition + state.Mouse.Position - state.Mouse.PositionMouseDown.GetValueOrDefault();
return base.OnDrag(state);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (args.Button == MouseButton.Left && AllowLeftClickDrag || args.Button == MouseButton.Right)
drag = false;
return base.OnMouseUp(state, args);
}
}
}
@@ -0,0 +1,80 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using Symcol.Core.Graphics.Containers;
namespace Symcol.Core.Graphics.UserInterface
{
/// <summary>
/// just a Button with a sprite
/// </summary>
public class SpriteButton : SymcolClickableContainer
{
private readonly string textureName;
public string Text
{
get { return spriteText?.Text; }
set
{
if (spriteText != null)
spriteText.Text = value;
}
}
private readonly Sprite sprite;
private readonly SpriteText spriteText;
public SpriteButton(string textureName)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
this.textureName = textureName;
Masking = true;
Children = new Drawable[]
{
sprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill
},
spriteText = new SpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = textures.Get(textureName);
}
protected override bool OnClick(InputState state)
{
if (Enabled.Value)
{
var flash = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f
};
Add(flash);
flash.Blending = BlendingMode.Additive;
flash.FadeOut(200);
flash.Expire();
}
return base.OnClick(state);
}
}
}
@@ -0,0 +1,158 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using Symcol.Core.Graphics.Containers;
namespace Symcol.Core.Graphics.UserInterface
{
public class SymcolWindow : SymcolContainer
{
/// <summary>
/// Put all your stuff in this
/// </summary>
public SymcolContainer WindowContent { get; set; }
public SpriteText WindowTitle;
private readonly SymcolContainer topBar;
private readonly SymcolClickableContainer minimize;
public SymcolWindow(Vector2 size)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
CornerRadius = 6;
Masking = true;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
topBar = new SymcolContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 20,
Width = size.X,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f
},
WindowTitle = new SpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 18
},
new SymcolClickableContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y,
Width = 30,
Action = Close,
Child = new Box
{
Colour = Color4.Red,
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f
}
},
minimize = new SymcolClickableContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y,
Width = 30,
Position = new Vector2(-30, 0),
Action = Minimize,
Child = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f
}
}
}
},
WindowContent = new SymcolContainer
{
Size = size,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
};
WindowContent.Position = new Vector2(0, topBar.Height);
}
protected void Close()
{
this.FadeOut(200);
}
protected void Open()
{
this.FadeIn(200);
}
public void Toggle()
{
if (Alpha > 0)
this.FadeOut(200);
else
this.FadeIn(200);
}
protected override bool OnDragStart(InputState state) => true;
private bool drag;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
if (args.Button == MouseButton.Left)
drag = true;
return base.OnMouseDown(state, args);
}
protected override bool OnDrag(InputState state)
{
if (drag)
Position += state.Mouse.Delta;
return base.OnDrag(state);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (args.Button == MouseButton.Left)
drag = false;
return base.OnMouseUp(state, args);
}
public void Maximize()
{
WindowContent.FadeIn(200);
WindowContent.ScaleTo(Vector2.One, 200);
minimize.Action = Minimize;
}
public void Minimize()
{
WindowContent.FadeOut(200);
WindowContent.ScaleTo(new Vector2(1, 0), 200);
minimize.Action = Maximize;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
namespace Symcol.Core.Networking
{
[Serializable]
public class BasicPacket : Packet
{
/// <summary>
/// Ask host if we can connect
/// </summary>
public bool Connect;
/// <summary>
/// Tell the host we are breaking up
/// </summary>
public bool Disconnect;
/// <summary>
/// Testing Connection
/// </summary>
public bool Test;
/// <summary>
/// Send a force exit to others
/// </summary>
public bool Abort;
/// <summary>
/// PreLoad the game
/// </summary>
public bool LoadGame;
/// <summary>
/// Request a list of all players from Host
/// </summary>
public bool RequestPlayerList;
/// <summary>
/// List of players in this match that we should account for
/// </summary>
public List<ClientInfo> PlayerList = new List<ClientInfo>();
/// <summary>
/// Tell Host we are PreLoaded
/// </summary>
public bool Loaded;
/// <summary>
/// Start the game already!
/// </summary>
public bool StartGame;
/// <summary>
/// Send to host when game started
/// </summary>
public bool GameStarted;
public BasicPacket(ClientInfo clientInfo) : base(clientInfo)
{
}
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
namespace Symcol.Core.Networking
{
/// <summary>
/// Just a client signature basically
/// </summary>
[Serializable]
public class ClientInfo
{
public string IP;
public int Port;
public int Ping;
public int ConncetionTryCount;
public double LastConnectionTime;
public double StartedTestConnectionTime;
}
}
+119
View File
@@ -0,0 +1,119 @@
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
namespace Symcol.Core.Networking
{
public class NetworkingClient
{
public UdpClient UdpClient;
public IPEndPoint EndPoint;
/// <summary>
/// if false we only receive
/// </summary>
public readonly bool Send;
public readonly int Port;
public readonly string IP;
public NetworkingClient(bool send, string ip, int port = 25570)
{
Port = port;
IP = ip;
if (send)
initializeSend();
else
initializeReceive();
}
private void initializeSend()
{
UdpClient = new UdpClient(IP, Port);
}
private void initializeReceive()
{
UdpClient = new UdpClient(Port);
EndPoint = new IPEndPoint(IPAddress.Any, Port);
}
private void sendByte(byte[] data)
{
UdpClient.Send(data, data.Length);
}
private byte[] receiveByte()
{
return UdpClient.Receive(ref EndPoint);
}
public static int SENTPACKETCOUNT;
/// <summary>
/// Send a Packet somewhere
/// </summary>
/// <param name="packet"></param>
public void SendPacket(Packet packet)
{
SENTPACKETCOUNT++;
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, packet);
stream.Position = 0;
int i = packet.PacketSize;
retry:
byte[] data = new byte[i];
try
{
stream.Read(data, 0, (int)stream.Length);
}
catch
{
i *= 2;
goto retry;
}
sendByte(data);
}
}
/// <summary>
/// Receive a Packet from somewhere
/// </summary>
/// <returns></returns>
public Packet ReceivePacket(bool force = false)
{
if (UdpClient.Available > 0 || force)
using (MemoryStream stream = new MemoryStream())
{
byte[] data = receiveByte();
stream.Write(data, 0, data.Length);
stream.Position = 0;
BinaryFormatter formatter = new BinaryFormatter();
Packet packet = (Packet)formatter.Deserialize(stream);
packet.ClientInfo.IP = EndPoint.Address.ToString();
return packet;
}
else
return null;
}
public void Clear()
{
if (UdpClient != null)
UdpClient.Dispose();
}
}
}
@@ -0,0 +1,646 @@
#define SoloTesting
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using System;
using System.Collections.Generic;
namespace Symcol.Core.Networking
{
//TODO: This NEEDS its own clock to avoid fuckery later on with DoubleTime and HalfTime
public class NetworkingClientHandler : Container
{
//30 Seconds by default
protected virtual double TimeOutTime => 30000;
protected readonly NetworkingClient ReceiveClient;
protected readonly NetworkingClient SendClient;
/// <summary>
/// Just a client signature basically
/// </summary>
public ClientInfo ClientInfo;
/// <summary>
/// All Connecting clients
/// </summary>
public readonly List<ClientInfo> ConnectingClients = new List<ClientInfo>();
/// <summary>
/// All Connected clients
/// </summary>
public readonly List<ClientInfo> ConncetedClients = new List<ClientInfo>();
/// <summary>
/// Clients waiting in our match
/// </summary>
public readonly List<ClientInfo> InMatchClients = new List<ClientInfo>();
/// <summary>
/// Clients loaded and ready to start
/// </summary>
public readonly List<ClientInfo> LoadedClients = new List<ClientInfo>();
/// <summary>
/// Clients ingame playing
/// </summary>
public readonly List<ClientInfo> InGameClients = new List<ClientInfo>();
/// <summary>
/// Gets hit when we get a Packet
/// </summary>
public Action<Packet> OnPacketReceive;
/// <summary>
/// (Peer) Call this when we connect to a Host (Includes list of connected peers + Host)
/// </summary>
public Action<List<ClientInfo>> OnConnectedToHost;
/// <summary>
/// (Host) Whenever a new client Connects
/// </summary>
public Action<ClientInfo> OnClientConnect;
/// <summary>
/// (Host) Whenever a new client Disconnects
/// </summary>
public Action<ClientInfo> OnClientDisconnect;
/// <summary>
/// (Host/Peer) When a new Client joins the game
/// </summary>
public Action<ClientInfo> OnClientJoin;
/// <summary>
/// Receive a full player list
/// </summary>
public Action<List<ClientInfo>> OnReceivePlayerList;
/// <summary>
/// if we are connected and in a match
/// </summary>
public bool InMatch;
/// <summary>
/// Are we in a game
/// </summary>
public bool InGame;
/// <summary>
/// Are we loaded and ready to start?
/// </summary>
public bool Loaded;
/// <summary>
/// Called to leave an in-progress game
/// </summary>
public Action OnAbort;
/// <summary>
/// Called to load the game (Includes Host)
/// </summary>
public Action<List<ClientInfo>> OnLoadGame;
/// <summary>
/// Called to start the game once loaded
/// </summary>
public Action StartGame;
public readonly ClientType ClientType;
public NetworkingClientHandler(ClientType type, string ip, int port = 25570, string thisLocalIp = "0.0.0.0")
{
AlwaysPresent = true;
ClientType = type;
switch (type)
{
case ClientType.Host:
ReceiveClient = new NetworkingClient(false, ip, port);
break;
case ClientType.Peer:
ReceiveClient = new NetworkingClient(false, thisLocalIp, port);
SendClient = new NetworkingClient(true, ip, port);
break;
case ClientType.Server:
throw new NotImplementedException();
}
Logger.Log("Created a RulesetNetworkingClientHandler", LoggingTarget.Network, LogLevel.Verbose);
if (ClientInfo == null)
ClientInfo = new ClientInfo
{
Port = port
};
}
protected override void LoadComplete()
{
base.LoadComplete();
if (ClientType == ClientType.Peer)
ConnectToHost();
}
protected override void Update()
{
base.Update();
PacketRestart:
Packet p = null;
if (ReceiveClient.UdpClient.Available > 0)
p = ReceiveClient.ReceivePacket();
if (p is BasicPacket packet)
{
//Hosts
if (SendClient == null)
{
if (packet.Disconnect)
{
OnClientDisconnect?.Invoke(packet.ClientInfo);
foreach (ClientInfo client in ConnectingClients)
if (client.IP == packet.ClientInfo.IP)
{
ConnectingClients.Remove(client);
Logger.Log("A Connecting Client has Disconnected", LoggingTarget.Network, LogLevel.Verbose);
break;
}
foreach (ClientInfo client in ConncetedClients)
if (client.IP == packet.ClientInfo.IP)
{
ConncetedClients.Remove(client);
Logger.Log("A Client has Disconnected", LoggingTarget.Network, LogLevel.Verbose);
break;
}
foreach (ClientInfo client in InMatchClients)
if (client.IP == packet.ClientInfo.IP)
{
InMatchClients.Remove(client);
break;
}
foreach (ClientInfo client in LoadedClients)
if (client.IP == packet.ClientInfo.IP)
{
LoadedClients.Remove(client);
break;
}
foreach (ClientInfo client in InGameClients)
if (client.IP == packet.ClientInfo.IP)
{
InGameClients.Remove(client);
break;
}
}
if (packet.Connect)
{
packet.ClientInfo.StartedTestConnectionTime = Time.Current;
ConnectingClients.Add(packet.ClientInfo);
NetworkingClient client = new NetworkingClient(true, packet.ClientInfo.IP, packet.ClientInfo.Port);
List<ClientInfo> playerList = new List<ClientInfo>
{
ClientInfo
};
foreach (ClientInfo clientInfo in ConncetedClients)
playerList.Add(clientInfo);
client.SendPacket(new BasicPacket(ClientInfo)
{
PlayerList = playerList,
Connect = true
});
Logger.Log("A Client is Connecting. . .", LoggingTarget.Network, LogLevel.Verbose);
}
if (packet.RequestPlayerList)
{
NetworkingClient client = new NetworkingClient(true, packet.ClientInfo.IP, packet.ClientInfo.Port);
List<ClientInfo> playerList = new List<ClientInfo>
{
ClientInfo
};
foreach (ClientInfo clientInfo in ConncetedClients)
playerList.Add(clientInfo);
client.SendPacket(new BasicPacket(ClientInfo)
{
PlayerList = playerList,
RequestPlayerList = true
});
Logger.Log("A Client is Connecting. . .", LoggingTarget.Network, LogLevel.Verbose);
}
if (packet.Loaded)
foreach (ClientInfo client in InMatchClients)
if (client.IP == packet.ClientInfo.IP)
{
Logger.Log("A Client has Loaded and is ready to start", LoggingTarget.Network, LogLevel.Verbose);
InMatchClients.Remove(client);
LoadedClients.Add(client);
break;
}
if (packet.GameStarted)
foreach (ClientInfo client in LoadedClients)
if (client.IP == packet.ClientInfo.IP)
{
Logger.Log("A Client has started!", LoggingTarget.Network, LogLevel.Verbose);
LoadedClients.Remove(client);
InGameClients.Add(client);
break;
}
if (packet.Test)
{
foreach (ClientInfo client in ConnectingClients)
if (client.IP == packet.ClientInfo.IP)
{
client.Ping = (int)Time.Current - (int)client.StartedTestConnectionTime;
ConnectingClients.Remove(client);
ConncetedClients.Add(client);
InMatchClients.Add(client);
OnClientJoin?.Invoke(client);
client.LastConnectionTime = Time.Current;
client.ConncetionTryCount = 0;
Logger.Log("Successfully connected to a Client! Ping: " + client.Ping, LoggingTarget.Network, LogLevel.Verbose);
break;
}
foreach (ClientInfo client in ConncetedClients)
if (client.IP == packet.ClientInfo.IP)
{
client.Ping = (int)Time.Current - (int)client.StartedTestConnectionTime;
client.LastConnectionTime = Time.Current;
client.ConncetionTryCount = 0;
Logger.Log("Successfully maintained connection to a Client! Ping: " + client.Ping, LoggingTarget.Network, LogLevel.Verbose);
}
}
}
#if !SoloTesting
if (InMatchClients.Count == 0 && LoadedClients.Count > 0 && Loaded && !InGame)
SendStartGame();
#endif
//Peers
else if (SendClient != null)
{
if (packet.Connect)
{
if (!InGame && !InMatch)
{
InMatch = true;
OnConnectedToHost?.Invoke(packet.PlayerList);
}
Logger.Log("Connected to Host!", LoggingTarget.Network, LogLevel.Verbose);
}
if (packet.Test)
{
SendToHost(new BasicPacket(ClientInfo) { Test = true });
Logger.Log("Received connection test info from host, returning. . .", LoggingTarget.Network, LogLevel.Verbose);
}
if (packet.RequestPlayerList)
OnReceivePlayerList?.Invoke(packet.PlayerList);
if (packet.StartGame)
{
StartGame?.Invoke();
SendToHost(new BasicPacket(ClientInfo) { GameStarted = true });
InGame = true;
}
if (packet.Abort)
{
OnAbort?.Invoke();
InGame = false;
Loaded = false;
}
if (packet.LoadGame)
{
Logger.Log("Received instructions to LoadGame for " + packet.PlayerList.Count + " players", LoggingTarget.Network, LogLevel.Verbose);
OnLoadGame?.Invoke(packet.PlayerList);
}
}
}
#if SoloTesting
if (Loaded && !InGame)
SendStartGame();
#endif
if (p != null)
OnPacketReceive?.Invoke(p);
if (ReceiveClient.UdpClient.Available > 0)
goto PacketRestart;
foreach (ClientInfo client in ConnectingClients)
{
if (client.LastConnectionTime + TimeOutTime / 10 <= Time.Current && client.ConncetionTryCount == 0)
{
client.StartedTestConnectionTime = Time.Current;
TestConnection(client);
}
if (client.LastConnectionTime + TimeOutTime / 6 <= Time.Current && client.ConncetionTryCount == 1)
TestConnection(client);
if (client.LastConnectionTime + TimeOutTime / 3 <= Time.Current && client.ConncetionTryCount == 2)
TestConnection(client);
if (client.StartedTestConnectionTime + TimeOutTime <= Time.Current)
{
ConnectingClients.Remove(client);
Logger.Log("Connection to a connecting client lost! - " + client.IP + ":" + client.Port, LoggingTarget.Network, LogLevel.Error);
break;
}
}
foreach (ClientInfo client in ConncetedClients)
{
if (client.LastConnectionTime + TimeOutTime / 6 <= Time.Current && client.ConncetionTryCount == 0)
{
client.StartedTestConnectionTime = Time.Current;
TestConnection(client);
}
if (client.LastConnectionTime + TimeOutTime / 3 <= Time.Current && client.ConncetionTryCount == 1)
TestConnection(client);
if (client.LastConnectionTime + TimeOutTime / 2 <= Time.Current && client.ConncetionTryCount == 2)
TestConnection(client);
if (client.StartedTestConnectionTime + TimeOutTime <= Time.Current)
{
ConncetedClients.Remove(client);
InGameClients.Remove(client);
LoadedClients.Remove(client);
InGameClients.Remove(client);
Logger.Log("Connection to a connected client lost! - " + client.IP + ":" + client.Port, LoggingTarget.Network, LogLevel.Error);
break;
}
}
}
/// <summary>
/// Poke!
/// </summary>
/// <param name="clientInfo"></param>
protected void TestConnection(ClientInfo clientInfo)
{
clientInfo.ConncetionTryCount++;
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(new BasicPacket(ClientInfo) { Test = true });
Logger.Log("Testing a client's connection - " + clientInfo.IP + ":" + clientInfo.Port, LoggingTarget.Network, LogLevel.Verbose);
}
public void RequestPlayerList()
{
BasicPacket packet = new BasicPacket(ClientInfo) { RequestPlayerList = true };
SendToHost(packet);
}
/// <summary>
/// Tell peers to start loading game
/// </summary>
public virtual void StartLoadingGame()
{
if (SendClient == null)
{
BasicPacket packet = new BasicPacket(ClientInfo) { LoadGame = true };
foreach (ClientInfo client in InMatchClients)
packet.PlayerList.Add(client);
packet.PlayerList.Add(ClientInfo);
SendToInMatchClients(packet);
OnLoadGame?.Invoke(packet.PlayerList);
}
else
Logger.Log("Called StartLoadingGame - We are not the Host!", LoggingTarget.Network, LogLevel.Verbose);
}
/// <summary>
/// Call this when the game is Loaded and ready to be started
/// </summary>
public virtual void GameLoaded()
{
Loaded = true;
SendToHost(new BasicPacket(ClientInfo) { Loaded = true });
}
/// <summary>
/// Connects to the Host
/// </summary>
public virtual void ConnectToHost()
{
SendToHost(new BasicPacket(ClientInfo) { Connect = true });
Logger.Log("Attempting conection to Host. . .", LoggingTarget.Network, LogLevel.Verbose);
}
/// <summary>
/// Tell peers to start and starts ours
/// </summary>
public virtual void SendStartGame()
{
if (SendClient == null)
{
SendToLoadedClients(new BasicPacket(ClientInfo) { StartGame = true });
InGame = true;
Logger.Log("Sending Start Game", LoggingTarget.Network, LogLevel.Verbose);
}
StartGame?.Invoke();
}
/// <summary>
/// Send a Packet to the Host
/// </summary>
/// <param name="packet"></param>
public void SendToHost(Packet packet)
{
if (SendClient != null)
SendClient.SendPacket(packet);
}
/// <summary>
/// Send a Packet to all Connecting clients
/// </summary>
/// <param name="packet"></param>
public void SendToConnectingClients(Packet packet)
{
if (SendClient == null)
foreach (ClientInfo clientInfo in ConnectingClients)
{
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(packet);
}
}
/// <summary>
/// Send a Packet to all clients Connected and waiting
/// </summary>
/// <param name="packet"></param>
public void SendToConnectedClients(Packet packet)
{
if (SendClient == null)
foreach (ClientInfo clientInfo in ConncetedClients)
{
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(packet);
}
}
/// <summary>
/// Send a Packet to all clients In this Match
/// </summary>
/// <param name="packet"></param>
public void SendToInMatchClients(Packet packet)
{
if (SendClient == null)
foreach (ClientInfo clientInfo in InMatchClients)
{
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(packet);
}
}
/// <summary>
/// Send a Packet to all clients Loaded
/// </summary>
/// <param name="packet"></param>
public void SendToLoadedClients(Packet packet)
{
if (SendClient == null)
foreach (ClientInfo clientInfo in LoadedClients)
{
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(packet);
}
}
/// <summary>
/// Send a Packet to all clients InGame
/// </summary>
/// <param name="packet"></param>
public void SendToInGameClients(Packet packet)
{
if (SendClient == null)
foreach (ClientInfo clientInfo in InGameClients)
{
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(packet);
}
}
/// <summary>
/// Send a Packet to ALL clients we know
/// </summary>
/// <param name="packet"></param>
public void SendToAllClients(Packet packet)
{
if (SendClient == null)
{
SendToConnectingClients(packet);
SendToConnectedClients(packet);
}
}
/// <summary>
/// Send to all but the one that sent it
/// </summary>
/// <param name="packet"></param>
/// <param name="playerID"></param>
public void ShareWithOtherPeers(Packet packet)
{
if (SendClient == null)
foreach (ClientInfo clientInfo in ConncetedClients)
if (packet.ClientInfo.IP != clientInfo.IP)
{
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
client.SendPacket(packet);
}
}
public virtual void AbortGame()
{
SendToLoadedClients(new BasicPacket(ClientInfo) { Abort = true });
SendToInGameClients(new BasicPacket(ClientInfo) { Abort = true });
restart:
foreach (ClientInfo client in LoadedClients)
{
LoadedClients.Remove(client);
InMatchClients.Add(client);
goto restart;
}
foreach (ClientInfo client in InGameClients)
{
InGameClients.Remove(client);
InMatchClients.Add(client);
goto restart;
}
InGame = false;
Loaded = false;
OnAbort?.Invoke();
}
public virtual void Disconnect()
{
Packet packet = new BasicPacket(ClientInfo) { Disconnect = true };
OnAbort?.Invoke();
InMatch = false;
InGame = false;
Loaded = false;
if (SendClient == null)
{
SendToConnectingClients(packet);
SendToConnectedClients(packet);
}
else
SendToHost(packet);
}
/// <summary>
/// Die
/// </summary>
/// <param name="isDisposing"></param>
protected override void Dispose(bool isDisposing)
{
ReceiveClient?.Clear();
if (SendClient != null)
{
SendToHost(new BasicPacket(ClientInfo) { Disconnect = true });
SendClient.Clear();
}
base.Dispose(isDisposing);
}
}
public enum ClientType
{
Host,
Peer,
Server
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
namespace Symcol.Core.Networking
{
[Serializable]
public class Packet
{
/// <summary>
/// Just a Signature
/// </summary>
public readonly ClientInfo ClientInfo;
/// <summary>
/// Specify starting size of packet for efficiency
/// </summary>
public virtual int PacketSize => 1024;
public Packet(ClientInfo clientInfo)
{
ClientInfo = clientInfo;
}
}
}
+35
View File
@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("symcol.Toys")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("symcol.Toys")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f34ac16c-e590-4d70-a069-a748326852bf")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+95
View File
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F34AC16C-E590-4D70-A069-A748326852BF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Symcol.Core</RootNamespace>
<AssemblyName>Symcol.Core</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Cyotek.Drawing.BitmapFont, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58daa28b0b2de221, processorArchitecture=MSIL">
<HintPath>..\packages\Cyotek.Drawing.BitmapFont.1.3.4-beta1\lib\net46\Cyotek.Drawing.BitmapFont.dll</HintPath>
</Reference>
<Reference Include="ManagedBass, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ManagedBass.2.0.3\lib\net45\ManagedBass.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLite.Net.Platform.Generic, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLite.Net.Platform.Win32, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLiteNetExtensions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="GameObjects\SymcolHitbox.cs" />
<Compile Include="Graphics\Containers\SymcolClickableContainer.cs" />
<Compile Include="Graphics\Containers\SymcolDialContainer.cs" />
<Compile Include="Graphics\Containers\SymcolDragContainer.cs" />
<Compile Include="Graphics\Containers\SymcolContainer.cs" />
<Compile Include="Graphics\UserInterface\SpriteButton.cs" />
<Compile Include="Graphics\UserInterface\SymcolWindow.cs" />
<Compile Include="Networking\BasicPacket.cs" />
<Compile Include="Networking\ClientInfo.cs" />
<Compile Include="Networking\NetworkingClient.cs" />
<Compile Include="Networking\Packet.cs" />
<Compile Include="Networking\NetworkingClientHandler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-Framework\osu.Framework\osu.Framework.csproj">
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,43 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using System.Collections.Generic;
using System.Diagnostics;
namespace Symcol.Rulesets.Core.Containers
{
public class LinkText : OsuSpriteText, IHasTooltip
{
public string TooltipText => Tooltip;
public virtual string Tooltip => "";
private readonly OsuHoverContainer content;
public override bool HandleKeyboardInput => content.Action != null;
public override bool HandleMouseInput => content.Action != null;
protected override Container<Drawable> Content => content ?? (Container<Drawable>)this;
public override IEnumerable<Drawable> FlowingChildren => Children;
public string Url
{
set
{
if (value != null)
content.Action = () => Process.Start(value);
}
}
public LinkText()
{
AddInternal(content = new OsuHoverContainer
{
AutoSizeAxes = Axes.Both,
});
}
}
}
@@ -0,0 +1,24 @@
using osu.Game.Users;
namespace Symcol.Rulesets.Core.Containers
{
/// <summary>
/// TODO: make this more generic
/// </summary>
public class ProfileLink : LinkText
{
public override string Tooltip => "View profile in browser";
public ProfileLink(User user, bool maintainer = false)
{
if (!maintainer)
Text = "Ruleset Creator: " + user.Username;
else
Text = "Ruleset Maintainer: " + user.Username;
Url = $@"https://osu.ppy.sh/users/{user.Id}";
Font = @"Exo2.0-RegularItalic";
TextSize = 20;
}
}
}
@@ -0,0 +1,91 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using System;
using System.Collections.Generic;
namespace Symcol.Rulesets.Core.HitObjects
{
/// <summary>
/// Mostly stuff copied from Container
/// </summary>
/// <typeparam name="TObject"></typeparam>
public abstract class DrawableSymcolHitObject<TObject> : DrawableHitObject<TObject>
where TObject : HitObject
{
//Future prep?
//public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
//public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
protected virtual Container<Drawable> Content => new Container();
public IReadOnlyList<Drawable> Children
{
get
{
return InternalChildren;
}
set
{
ChildrenEnumerable = value;
}
}
public IEnumerable<Drawable> ChildrenEnumerable
{
set
{
Clear();
AddRange(value);
}
}
public void AddRange(IEnumerable<Drawable> range)
{
foreach (Drawable d in range)
Add(d);
}
public Drawable Child
{
get
{
if (Children.Count != 1)
throw new InvalidOperationException($"{nameof(Child)} is only available when there's only 1 in {nameof(Children)}!");
return Children[0];
}
set
{
Clear();
Add(value);
}
}
public void Clear() => Clear(true);
public virtual void Clear(bool disposeChildren)
{
if (Content != null)
Content.Clear(disposeChildren);
else
ClearInternal(disposeChildren);
}
protected DrawableSymcolHitObject(TObject hitObject)
: base(hitObject)
{
}
public void Add(Drawable drawable)
{
AddInternal(drawable);
}
public void Remove(Drawable drawable)
{
RemoveInternal(drawable);
}
}
}
@@ -0,0 +1,22 @@
using Symcol.Core.Networking;
using System;
namespace Symcol.Rulesets.Core.Multiplayer.Networking
{
[Serializable]
public class ChatPacket : Packet
{
public override int PacketSize => 4096;
public string Author;
public string AuthorColor;
public string Message;
public ChatPacket(ClientInfo clientInfo) : base(clientInfo)
{
}
}
}
@@ -0,0 +1,24 @@
using Symcol.Core.Networking;
using System;
namespace Symcol.Rulesets.Core.Multiplayer.Networking
{
/// <summary>
/// Just a client signature basically
/// </summary>
[Serializable]
public class RulesetClientInfo : ClientInfo
{
public string Username = "";
public int UserID = -1;
public string UserPic;
public string UserBackground;
public string UserCountry;
public string CountryFlagName;
}
}
@@ -0,0 +1,83 @@
using osu.Framework.Allocation;
using osu.Game;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using Symcol.Core.Networking;
using System;
namespace Symcol.Rulesets.Core.Multiplayer.Networking
{
//TODO: This NEEDS its own clock to avoid fuckery later on with DoubleTime and HalfTime
public class RulesetNetworkingClientHandler : NetworkingClientHandler, IOnlineComponent
{
public RulesetClientInfo RulesetClientInfo;
public Action<WorkingBeatmap> OnMapChange;
private OsuGame osu;
public RulesetNetworkingClientHandler(ClientType type, string ip, int port = 25570, string thisLocalIp = "0.0.0.0") : base(type, ip, port, thisLocalIp)
{
if (RulesetClientInfo == null)
{
RulesetClientInfo = new RulesetClientInfo
{
Port = port
};
ClientInfo = RulesetClientInfo;
}
}
/// <summary>
/// Send Map to Peers
/// </summary>
/// <param name="map"></param>
public void SetMap(WorkingBeatmap map)
{
RulesetPacket packet;
try
{
packet = new RulesetPacket(RulesetClientInfo)
{
OnlineBeatmapSetID = (int)map.BeatmapSetInfo.OnlineBeatmapSetID,
OnlineBeatmapID = (int)map.BeatmapInfo.OnlineBeatmapID
};
SendToInMatchClients(packet);
OnMapChange?.Invoke(osu.Beatmap.Value);
}
catch
{
packet = new RulesetPacket(RulesetClientInfo);
SendToInMatchClients(packet);
return;
}
}
[BackgroundDependencyLoader]
private void load(APIAccess api, OsuGame osu)
{
api.Register(this);
this.osu = osu;
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
default:
RulesetClientInfo.Username = "";
RulesetClientInfo.UserID = -1;
break;
case APIState.Online:
RulesetClientInfo.Username = api.LocalUser.Value.Username;
RulesetClientInfo.UserID = (int)api.LocalUser.Value.Id;
RulesetClientInfo.UserCountry = api.LocalUser.Value.Country.FullName;
RulesetClientInfo.CountryFlagName = api.LocalUser.Value.Country.FlagName;
RulesetClientInfo.UserPic = api.LocalUser.Value.AvatarUrl;
RulesetClientInfo.UserBackground = api.LocalUser.Value.CoverUrl;
break;
}
}
}
}
@@ -0,0 +1,28 @@
using Symcol.Core.Networking;
using System;
namespace Symcol.Rulesets.Core.Multiplayer.Networking
{
[Serializable]
public class RulesetPacket : Packet
{
public new readonly RulesetClientInfo ClientInfo;
public override int PacketSize => 4096;
public int OnlineBeatmapSetID = -1;
public int OnlineBeatmapID = -1;
public bool HaveMap;
public string ChatContent;
//public string RulesetName = "";
public RulesetPacket(RulesetClientInfo rulesetClientInfo) : base(rulesetClientInfo)
{
ClientInfo = rulesetClientInfo;
}
}
}
@@ -0,0 +1,22 @@
using Symcol.Core.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Symcol.Rulesets.Core.Multiplayer.Networking
{
[Serializable]
public class ScorePacket : Packet
{
public override int PacketSize => 2048;
public int Score;
public ScorePacket(ClientInfo clientInfo, int score) : base(clientInfo)
{
Score = score;
}
}
}
@@ -0,0 +1,43 @@
using osu.Framework.Configuration;
using osu.Game.Overlays.Settings;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace Symcol.Rulesets.Core.Multiplayer.Options
{
public class MultiplayerDropdownEnumOption<T> : MultiplayerOption
where T : struct
{
public readonly Bindable<T> BindableEnum;
public MultiplayerDropdownEnumOption(Bindable<T> bindable, string name, int quadrant, bool sync = true) : base(name, quadrant, sync)
{
BindableEnum = bindable;
OptionContainer.Child = new BetterSettingsEnumDropdown<T>
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Bindable = bindable,
};
}
private class BetterSettingsEnumDropdown<T> : SettingsEnumDropdown<T>
{
protected override Drawable CreateControl() => new BetterOsuEnumDropdown<T>
{
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
};
private class BetterOsuEnumDropdown<T> : OsuEnumDropdown<T>
{
public BetterOsuEnumDropdown()
{
Menu.MaxHeight = 160;
}
}
}
}
}
@@ -0,0 +1,87 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using System;
namespace Symcol.Rulesets.Core.Multiplayer.Options
{
public abstract class MultiplayerOption : Container
{
protected readonly SpriteText Title;
protected readonly Container OptionContainer;
public MultiplayerOption(string name, int quadrant, bool sync = true)
{
if (quadrant == 1 | quadrant == 3 | quadrant == 5 | quadrant == 7)
{
switch (quadrant)
{
case 1:
quadrant = 0;
break;
case 3:
quadrant = 1;
break;
case 5:
quadrant = 2;
break;
case 7:
quadrant = 3;
break;
}
Anchor = Anchor.TopLeft;
Origin = Anchor.TopLeft;
Position = new Vector2(16, 4 + (64 * quadrant));
}
else if (quadrant == 2 | quadrant == 4 | quadrant == 6 | quadrant == 8)
{
switch (quadrant)
{
case 2:
quadrant = 0;
break;
case 4:
quadrant = 1;
break;
case 6:
quadrant = 2;
break;
case 8:
quadrant = 3;
break;
}
Anchor = Anchor.TopCentre;
Origin = Anchor.TopLeft;
Position = new Vector2(22, 4 + (64 * quadrant));
}
else
throw new Exception("Globglogabgalab");
RelativeSizeAxes = Axes.X;
Width = 0.49f;
Height = 80;
Children = new Drawable[]
{
Title = new SpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
TextSize = 20,
Text = name
},
OptionContainer = new Container
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(-16, 18),
RelativeSizeAxes = Axes.Both,
}
};
}
}
}
@@ -0,0 +1,27 @@
using osu.Framework.Configuration;
using osu.Game.Overlays.Settings;
using osu.Framework.Graphics;
using OpenTK;
namespace Symcol.Rulesets.Core.Multiplayer.Options
{
public class MultiplayerToggleOption : MultiplayerOption
{
public readonly Bindable<bool> BindableBool;
public MultiplayerToggleOption(Bindable<bool> bindable, string name, int quadrant, bool sync = true) : base(name, quadrant, sync)
{
BindableBool = bindable;
Child = new SettingsCheckbox
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Bindable = bindable,
LabelText = " " + name,
Position = new Vector2(-16, 18),
};
}
}
}
@@ -0,0 +1,148 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Users;
using Symcol.Core.Networking;
using Symcol.Rulesets.Core.Multiplayer.Networking;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class Chat : Container, IOnlineComponent
{
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
private string playerColorHex = SymcolSettingsSubsection.SymcolConfigManager.GetBindable<string>(SymcolSetting.PlayerColor);
private User user;
private readonly FillFlowContainer<ChatMessage> messageContainer;
private readonly OsuTextBox textBox;
public Chat(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
{
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
rulesetNetworkingClientHandler.OnPacketReceive += (packet) =>
{
if (packet is ChatPacket chatPacket)
Add(chatPacket);
if (rulesetNetworkingClientHandler.ClientType == ClientType.Host)
rulesetNetworkingClientHandler.ShareWithOtherPeers(packet);
};
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
RelativeSizeAxes = Axes.Both;
Height = 0.46f;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.8f
},
new OsuScrollContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.9f,
Children = new Drawable[]
{
messageContainer = new FillFlowContainer<ChatMessage>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
}
}
},
textBox = new OsuTextBox
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Width = 0.98f,
Height = 36,
Position = new Vector2(0, -12),
Colour = Color4.White,
Text = "Type here!"
}
};
textBox.OnCommit += (s, r) =>
{
AddMessage(textBox.Text);
textBox.Text = "";
};
}
public void Add(ChatPacket packet)
{
ChatMessage message = new ChatMessage(packet);
messageContainer.Add(message);
}
public void AddMessage(string message)
{
if (message == "" | message == " ")
return;
if (user != null)
{
try
{
OsuColour.FromHex(playerColorHex);
}
catch
{
playerColorHex = "#ffffff";
}
ChatPacket packet = new ChatPacket(rulesetNetworkingClientHandler.ClientInfo)
{
Author = user.Username,
AuthorColor = playerColorHex,
Message = message,
};
rulesetNetworkingClientHandler.SendToHost(packet);
rulesetNetworkingClientHandler.SendToInMatchClients(packet);
Add(packet);
}
else
Logger.Log("You must be logged in to message!", LoggingTarget.Network, LogLevel.Error);
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
api.Register(this);
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
default:
user = null;
break;
case APIState.Online:
user = api.LocalUser.Value;
break;
}
}
}
}
@@ -0,0 +1,42 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using Symcol.Rulesets.Core.Multiplayer.Networking;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class ChatMessage : Container
{
public ChatMessage(ChatPacket packet)
{
Anchor = Anchor.TopLeft;
Origin = Anchor.TopLeft;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = OsuColour.FromHex(packet.AuthorColor),
TextSize = 24,
Text = packet.Author + ":"
},
new OsuTextFlowContainer(t => { t.TextSize = 24; })
{
Position = new OpenTK.Vector2(140, 0),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Text = packet.Message
}
};
}
}
}
@@ -0,0 +1,123 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Users;
using Symcol.Rulesets.Core.Multiplayer.Networking;
using System.Diagnostics;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class MatchPlayer : ClickableContainer, IHasContextMenu
{
public readonly RulesetClientInfo ClientInfo;
private readonly Box dim;
private readonly DrawableFlag countryFlag;
private readonly UserCoverBackground profileBackground;
private readonly UpdateableAvatar profilePicture;
public MatchPlayer(RulesetClientInfo clientInfo)
{
ClientInfo = clientInfo;
Alpha = 0;
Masking = true;
RelativeSizeAxes = Axes.X;
Height = 40f;
CornerRadius = 10;
Country country = new Country
{
FullName = ClientInfo.UserCountry,
FlagName = ClientInfo.CountryFlagName,
};
User user = new User
{
Username = ClientInfo.Username,
Id = ClientInfo.UserID,
Country = country,
AvatarUrl = ClientInfo.UserPic,
CoverUrl = ClientInfo.UserBackground,
};
Children = new Drawable[]
{
profileBackground = new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200),
},
dim = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.8f
},
profilePicture = new UpdateableAvatar
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(Height * 0.8f),
Position = new Vector2(6, 0),
User = user,
Masking = true,
CornerRadius = 6,
},
countryFlag = new DrawableFlag(country)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Size = new Vector2(Height * 0.9f, (Height * 0.9f) * 0.66f),
Position = new Vector2(-10, 0)
},
new SpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Position = new Vector2(Height * 1.1f, 0),
TextSize = Height * 0.9f,
Text = user.Username
}
};
Action = () =>
{
Process.Start("https://osu.ppy.sh/users/" + user.Id);
};
}
protected override bool OnHover(InputState state)
{
dim.FadeTo(0.6f, 200);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
dim.FadeTo(0.8f, 200);
}
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem("View Profile", MenuItemType.Standard, () => { }),
new OsuMenuItem("Promote to Host", MenuItemType.Highlighted, () => { }),
new OsuMenuItem("Kick", MenuItemType.Destructive, () => { }),
new OsuMenuItem("Ban", MenuItemType.Destructive, () => { }),
};
}
}
@@ -0,0 +1,110 @@
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using System.Collections.Generic;
using osu.Framework.Graphics;
using OpenTK;
using Symcol.Core.Networking;
using Symcol.Rulesets.Core.Multiplayer.Networking;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class MatchPlayerList : Container
{
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
public readonly List<MatchPlayer> MatchPlayers = new List<MatchPlayer>();
public readonly FillFlowContainer MatchPlayersContianer;
public MatchPlayerList(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
{
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
Masking = true;
CornerRadius = 16;
Anchor = Anchor.TopLeft;
Origin = Anchor.TopLeft;
RelativeSizeAxes = Axes.Both;
Width = 0.49f;
Height = 0.45f;
Position = new Vector2(10);
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.8f)
},
MatchPlayersContianer = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.98f,
Height = 0.96f
}
};
rulesetNetworkingClientHandler.OnReceivePlayerList += (players) =>
{
restart:
foreach (MatchPlayer matchPlayer in MatchPlayers)
foreach (ClientInfo clientInfo in players)
if (clientInfo is RulesetClientInfo rulesetClientInfo)
if (rulesetClientInfo.IP + rulesetClientInfo.Port != matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
{
Add(rulesetClientInfo);
players.Remove(clientInfo);
goto restart;
}
};
rulesetNetworkingClientHandler.RequestPlayerList();
rulesetNetworkingClientHandler.OnClientJoin += (clientInfo) =>
{
foreach (MatchPlayer matchPlayer in MatchPlayers)
if (clientInfo is RulesetClientInfo rulesetClientInfo)
if (rulesetClientInfo.IP + rulesetClientInfo.Port != matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
{
Add(rulesetClientInfo);
break;
}
};
rulesetNetworkingClientHandler.OnClientDisconnect += (clientInfo) =>
{
foreach (MatchPlayer matchPlayer in MatchPlayers)
if (clientInfo is RulesetClientInfo rulesetClientInfo)
if (rulesetClientInfo.IP + rulesetClientInfo.Port == matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
{
Remove(matchPlayer);
break;
}
};
}
public void Add(RulesetClientInfo clientInfo)
{
MatchPlayer matchPlayer = new MatchPlayer(clientInfo);
Add(matchPlayer);
}
public void Add(MatchPlayer matchPlayer)
{
MatchPlayers.Add(matchPlayer);
MatchPlayersContianer.Add(matchPlayer);
matchPlayer.FadeInFromZero(200);
}
public void Remove(MatchPlayer matchPlayer)
{
MatchPlayers.Remove(matchPlayer);
matchPlayer.FadeOutFromOne(200)
.Expire();
}
}
}
@@ -0,0 +1,372 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Symcol.Pieces;
using Symcol.Rulesets.Core.Multiplayer.Options;
using System;
using System.Diagnostics;
using System.Linq;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class MatchTools : Container
{
public readonly Bindable<MatchScreenMode> Mode = new Bindable<MatchScreenMode>() { Default = MatchScreenMode.MapDetails };
public readonly Bindable<MatchGamemode> GameMode = new Bindable<MatchGamemode>() { Default = MatchGamemode.HeadToHead };
public readonly OsuTabControl<MatchScreenMode> TabControl;
public readonly Container SelectedContent;
public readonly Container MapDetails;
public Container RulesetSettings;
public readonly Container SoundBoard;
private WorkingBeatmap selectedBeatmap;
private int selectedBeatmapSetID;
public MatchTools()
{
Masking = true;
CornerRadius = 16;
Anchor = Anchor.TopRight;
Origin = Anchor.TopRight;
RelativeSizeAxes = Axes.Both;
Width = 0.49f;
Height = 0.45f;
Position = new Vector2(-10, 10);
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.8f)
},
TabControl = new OsuTabControl<MatchScreenMode>
{
Position = new Vector2(72, 0),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.08f,
Width = 0.8f
},
SelectedContent = new Container
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.92f
}
};
TabControl.Current.Value = MatchScreenMode.MapDetails;
Mode.ValueChanged += (value) =>
{
switch (value)
{
case MatchScreenMode.MapDetails:
if (selectedBeatmap != null)
SelectedContent.Child = new MapDetailsSection(selectedBeatmap);
else if (selectedBeatmapSetID != 0)
SelectedContent.Child = new MapDetailsSection(selectedBeatmapSetID);
else
SelectedContent.Child = new MapDetailsSection(true);
break;
case MatchScreenMode.MatchSettings:
SelectedContent.Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new MultiplayerDropdownEnumOption<MatchGamemode>(GameMode, "Match Gamemode", 1)
}
};
break;
case MatchScreenMode.SoundBoard:
SelectedContent.Child = new Container
{
RelativeSizeAxes = Axes.Both,
Child = new HitSoundBoard
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ButtonSize = 80
}
};
break;
}
};
Mode.BindTo(TabControl.Current);
}
public void MapChange(WorkingBeatmap workingBeatmap)
{
if (workingBeatmap == null)
{
MapChange(-1);
return;
}
selectedBeatmap = workingBeatmap;
selectedBeatmapSetID = (int)workingBeatmap.BeatmapSetInfo.OnlineBeatmapSetID;
if (Mode.Value == MatchScreenMode.MapDetails)
SelectedContent.Child = new MapDetailsSection(selectedBeatmap);
}
public void MapChange(int onlineBeatmapSetID)
{
selectedBeatmap = null;
selectedBeatmapSetID = onlineBeatmapSetID;
if (Mode.Value == MatchScreenMode.MapDetails)
{
if (selectedBeatmapSetID != 0 && selectedBeatmapSetID != -1)
SelectedContent.Child = new MapDetailsSection(selectedBeatmapSetID);
else
SelectedContent.Child = new MapDetailsSection(true);
}
}
}
public class MapDetailsSection : ClickableContainer
{
private Sprite beatmapBG;
private SpriteText name;
private SpriteText artist;
private SpriteText difficulty;
private SpriteText time;
private Box dim;
public MapDetailsSection(WorkingBeatmap workingBeatmap)
{
draw();
HitObject lastObject = workingBeatmap.Beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
beatmapBG.Texture = workingBeatmap.Background;
name.Text = workingBeatmap.BeatmapSetInfo.Metadata.Title;
artist.Text = "By: " + workingBeatmap.BeatmapSetInfo.Metadata.Artist;
difficulty.Text = workingBeatmap.BeatmapInfo.Version + " (" + Math.Round(workingBeatmap.BeatmapInfo.StarDifficulty, 2) + " stars) mapped by " + workingBeatmap.BeatmapInfo.Metadata.AuthorString;
time.Text = getBPMRange(workingBeatmap.Beatmap) + " bpm for " + TimeSpan.FromMilliseconds(endTime - workingBeatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss");
BorderColour = getColour(workingBeatmap.BeatmapInfo);
EdgeEffect = new EdgeEffectParameters
{
Radius = 16,
Type = EdgeEffectType.Shadow,
Colour = getColour(workingBeatmap.BeatmapInfo).Opacity(0.2f)
};
Action = () => Process.Start("https://osu.ppy.sh/beatmapsets/" + workingBeatmap.BeatmapSetInfo.OnlineBeatmapSetID);
}
public MapDetailsSection(int onlineBeatmapSetID)
{
draw();
name.Text = "Missing Map!";
artist.Text = "Click to open in Browser";
Action = () => Process.Start("https://osu.ppy.sh/beatmapsets/" + onlineBeatmapSetID);
}
public MapDetailsSection(bool invalid)
{
draw();
name.Text = "Invalid / No Map Selected!";
artist.Text = "Don't hit start, weird things might happen";
Action = () => Process.Start("https://osu.ppy.sh/home");
}
private void draw()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Width = 0.95f;
Height = 0.9f;
Masking = true;
BorderColour = Color4.LightBlue;
BorderThickness = 4;
CornerRadius = 10;
EdgeEffect = new EdgeEffectParameters
{
Radius = 16,
Type = EdgeEffectType.Shadow,
Colour = Color4.LightBlue.Opacity(0.2f)
};
Children = new Drawable[]
{
beatmapBG = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
},
dim = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f
},
name = new SpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, 0),
Font = @"Exo2.0-SemiBoldItalic",
TextSize = 40
},
artist = new SpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, 38),
Font = @"Exo2.0-MediumItalic",
TextSize = 24
},
difficulty = new SpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, 64),
Font = "Exo2.0-Bold",
TextSize = 16
},
time = new SpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, 84),
TextSize = 16
}
};
}
protected override bool OnHover(InputState state)
{
dim.FadeTo(0.4f, 200);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
dim.FadeTo(0.6f, 200);
}
//"Borrowed" stuff
private string getBPMRange(Beatmap beatmap)
{
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
if (Precision.AlmostEquals(bpmMin, bpmMax))
return $"{bpmMin:0}";
return $"{bpmMin:0}-{bpmMax:0} (mostly {beatmap.ControlPointInfo.BPMMode:0})";
}
private enum DifficultyRating
{
Easy,
Normal,
Hard,
Insane,
Expert,
ExpertPlus
}
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
var rating = beatmap.StarDifficulty;
if (rating < 1.5) return DifficultyRating.Easy;
if (rating < 2.25) return DifficultyRating.Normal;
if (rating < 3.75) return DifficultyRating.Hard;
if (rating < 5.25) return DifficultyRating.Insane;
if (rating < 6.75) return DifficultyRating.Expert;
return DifficultyRating.ExpertPlus;
}
private Color4 getColour(BeatmapInfo beatmap)
{
OsuColour palette = new OsuColour();
switch (getDifficultyRating(beatmap))
{
case DifficultyRating.Easy:
return palette.Green;
default:
case DifficultyRating.Normal:
return palette.Blue;
case DifficultyRating.Hard:
return palette.Yellow;
case DifficultyRating.Insane:
return palette.Pink;
case DifficultyRating.Expert:
return palette.Purple;
case DifficultyRating.ExpertPlus:
return palette.Gray0;
}
}
}
public enum MatchGamemode
{
[System.ComponentModel.Description("Head to Head")]
HeadToHead,
[System.ComponentModel.Description("Head to Head with Live Spectator")]
HeadToHeadSpectator,
[System.ComponentModel.Description("Team Versus")]
TeamVS,
[System.ComponentModel.Description("TAG4")]
TAG4,
[System.ComponentModel.Description("Team TAG4")]
TeamTAG4,
[System.ComponentModel.Description("Tourny Mode")]
Tournement,
}
public enum MatchScreenMode
{
[System.ComponentModel.Description("Map Details")]
MapDetails,
[System.ComponentModel.Description("Match Settings")]
MatchSettings,
[System.ComponentModel.Description("Ruleset Settings")]
RulesetSettings,
[System.ComponentModel.Description("Sound Board")]
SoundBoard
}
}
@@ -0,0 +1,93 @@
using OpenTK;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Rulesets.Scoring;
using Symcol.Core.Networking;
using Symcol.Rulesets.Core.Multiplayer.Networking;
using System.Collections.Generic;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class MultiplayerScoreboard : Container
{
public readonly Container<MultiplayerScoreboardItem> ScoreboardItems;
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
private readonly ScoreProcessor scoreProcessor;
private double updateScoreTime = 0;
public MultiplayerScoreboard(RulesetNetworkingClientHandler rulesetNetworkingClientHandler, List<ClientInfo> playerList, ScoreProcessor scoreProcessor)
{
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
this.scoreProcessor = scoreProcessor;
AlwaysPresent = true;
AutoSizeAxes = Axes.Y;
Width = 120;
Position = new Vector2(0, -200);
Anchor = Anchor.CentreLeft;
Origin = Anchor.TopLeft;
Child = ScoreboardItems = new Container<MultiplayerScoreboardItem>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
};
int i = 1;
foreach (ClientInfo clientInfo in playerList)
{
if (clientInfo is RulesetClientInfo rulesetClientInfo)
{
ScoreboardItems.Add(new MultiplayerScoreboardItem(rulesetClientInfo, i) { });
i++;
}
}
rulesetNetworkingClientHandler.OnPacketReceive += (Packet packet) =>
{
if (packet is ScorePacket scorePacket)
{
rulesetNetworkingClientHandler.ShareWithOtherPeers(scorePacket);
foreach (MultiplayerScoreboardItem item in ScoreboardItems)
if (scorePacket.ClientInfo == item.ClientInfo)
item.Score = scorePacket.Score;
}
};
}
protected override void Update()
{
base.Update();
if (Time.Current >= updateScoreTime)
{
updateScoreTime = Time.Current + 500;
foreach (MultiplayerScoreboardItem item in ScoreboardItems)
if (rulesetNetworkingClientHandler.ClientInfo == item.ClientInfo)
item.Score = (int)scoreProcessor.TotalScore.Value;
rulesetNetworkingClientHandler.SendToHost(new ScorePacket(rulesetNetworkingClientHandler.ClientInfo, (int)scoreProcessor.TotalScore.Value));
rulesetNetworkingClientHandler.SendToInGameClients(new ScorePacket(rulesetNetworkingClientHandler.ClientInfo, (int)scoreProcessor.TotalScore.Value));
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key.Tab)
{
if (Alpha > 0)
this.FadeOut(100);
else
this.FadeIn(100);
}
return base.OnKeyDown(state, args);
}
}
}
@@ -0,0 +1,107 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using Symcol.Rulesets.Core.Multiplayer.Networking;
using System.Collections.Generic;
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
{
public class MultiplayerScoreboardItem : Container
{
public int Score
{
get { return score; }
set
{
if (value != score)
{
score = value;
scoreText.Text = value.ToString();
foreach(MultiplayerScoreboardItem item in itemList)
if (value > item.Score && Place > item.Place)
{
Place = item.Place;
foreach (MultiplayerScoreboardItem i in itemList)
if (i.Place < Place)
i.Place -= 1;
}
}
}
}
public int Place
{
get { return place; }
set
{
if (Place != place)
{
place = value;
this.MoveTo(new Vector2(0, (-height - 8) * (value - 1)), 200, Easing.OutQuint);
}
}
}
private int place = 0;
private int score = 0;
private const int height = 60;
public readonly RulesetClientInfo ClientInfo;
private readonly SpriteText scoreText;
private static List<MultiplayerScoreboardItem> itemList = new List<MultiplayerScoreboardItem>();
public MultiplayerScoreboardItem(RulesetClientInfo clientInfo, int place)
{
ClientInfo = clientInfo;
this.place = place;
itemList.Add(this);
RelativeSizeAxes = Axes.X;
Height = height;
Masking = true;
CornerRadius = 8;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.8f,
},
new SpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(4),
Text = clientInfo.Username
},
scoreText = new SpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Position = new Vector2(-4),
Text = Score.ToString()
}
};
this.MoveTo(new Vector2(0, (-height - 8) * (Place - 1)), 200, Easing.OutQuint);
}
protected override void Dispose(bool isDisposing)
{
itemList.Remove(this);
base.Dispose(isDisposing);
}
}
}
@@ -0,0 +1,47 @@
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
using System;
using osu.Game.Screens;
using osu.Framework.Screens;
using Symcol.Rulesets.Core.Multiplayer.Networking;
namespace Symcol.Rulesets.Core.Multiplayer.Screens
{
public class MatchSongSelect : SongSelect
{
public WorkingBeatmap SelectedMap;
private bool exiting;
protected override BackgroundScreen CreateBackground() => null;
public Action Action;
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
public MatchSongSelect(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
{
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
}
protected override void OnEntering(Screen last)
{
Add(RulesetNetworkingClientHandler);
base.OnEntering(last);
}
protected override bool OnSelectionFinalised()
{
if (!exiting)
{
RulesetNetworkingClientHandler.OnMapChange?.Invoke(null);
SelectedMap = Beatmap.Value;
Action();
exiting = true;
Remove(RulesetNetworkingClientHandler);
Exit();
}
return true;
}
}
}
@@ -0,0 +1,383 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Ranking;
using osu.Game.Storyboards.Drawables;
using OpenTK;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using OpenTK.Input;
using Symcol.Rulesets.Core.Multiplayer.Networking;
using Symcol.Rulesets.Core.Multiplayer.Pieces;
using System.Collections.Generic;
using Symcol.Core.Networking;
namespace Symcol.Rulesets.Core.Multiplayer.Screens
{
public class MultiPlayer : ScreenWithBeatmapBackground, IProvideCursor
{
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
protected override float BackgroundParallaxAmount => 0.1f;
public override bool ShowOverlaysOnEnter => false;
public Action RestartRequested;
public override bool AllowBeatmapRulesetChange => false;
public bool HasFailed { get; private set; }
public bool AllowPause { get; set; } = false;
public bool AllowLeadIn { get; set; } = true;
public bool AllowResults { get; set; } = true;
public int RestartCount;
public CursorContainer Cursor => RulesetContainer.Cursor;
public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded.Value;
private IAdjustableClock sourceClock;
private DecoupleableInterpolatingFramedClock adjustableClock;
private RulesetInfo ruleset;
private APIAccess api;
private ScoreProcessor scoreProcessor;
protected RulesetContainer RulesetContainer;
#region User Settings
private Bindable<double> dimLevel;
private Bindable<double> blurLevel;
private Bindable<bool> showStoryboard;
private Bindable<bool> mouseWheelDisabled;
private Bindable<double> userAudioOffset;
private SampleChannel sampleRestart;
#endregion
private Container storyboardContainer;
private DrawableStoryboard storyboard;
private HUDOverlay hudOverlay;
private MultiplayerScoreboard scoreboard;
private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true;
private readonly List<ClientInfo> playerList;
public MultiPlayer(RulesetNetworkingClientHandler rulesetNetworkingClientHandler, List<ClientInfo> playerList)//, WorkingBeatmap beatmap = null)
{
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
RulesetNetworkingClientHandler.OnAbort = () => Exit();
RulesetNetworkingClientHandler.StartGame = () => start();
this.playerList = playerList;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
{
this.api = api;
dimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
blurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
sampleRestart = audio.Sample.Get(@"Gameplay/restart");
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
WorkingBeatmap working = Beatmap.Value;
Beatmap beatmap;
try
{
beatmap = working.Beatmap;
if (beatmap == null)
throw new InvalidOperationException("Beatmap was not loaded");
ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
var rulesetInstance = ruleset.CreateInstance();
try
{
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
}
catch (BeatmapInvalidForRulesetException)
{
// we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset
// let's try again forcing the beatmap's ruleset.
ruleset = beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance();
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true);
}
if (!RulesetContainer.Objects.Any())
throw new InvalidOperationException("Beatmap contains no hit objects!");
}
catch (Exception e)
{
Logger.Error(e, "Could not load beatmap sucessfully!");
//couldn't load, hard abort!
Exit();
return;
}
sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
var firstObjectTime = RulesetContainer.Objects.First().StartTime;
adjustableClock.Seek(AllowLeadIn
? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
: firstObjectTime);
adjustableClock.ProcessFrame();
// the final usable gameplay clock with user-set offsets applied.
var offsetClock = new FramedOffsetClock(adjustableClock);
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
scoreProcessor = RulesetContainer.CreateScoreProcessor();
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
Children = new Drawable[]
{
storyboardContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
RulesetContainer,
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
{
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
ProcessCustomClock = false,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ProcessCustomClock = false,
Breaks = beatmap.Breaks
},
scoreboard = new MultiplayerScoreboard(RulesetNetworkingClientHandler, playerList, scoreProcessor)
}
}
};
if (showStoryboard)
initializeStoryboard(false);
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToScoreProcessor>())
mod.ApplyToScoreProcessor(scoreProcessor);
}
private void applyRateFromMods()
{
if (sourceClock == null) return;
sourceClock.Rate = 1;
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(sourceClock);
}
private void initializeStoryboard(bool asyncLoad)
{
if (storyboardContainer == null)
return;
var beatmap = Beatmap.Value;
storyboard = beatmap.Storyboard.CreateDrawable();
storyboard.Masking = true;
if (asyncLoad)
LoadComponentAsync(storyboard, storyboardContainer.Add);
else
storyboardContainer.Add(storyboard);
}
private ScheduledDelegate onCompletionEvent;
private void onCompletion()
{
// Only show the completion screen if the player hasn't failed
if (scoreProcessor.HasFailed || onCompletionEvent != null)
return;
ValidForResume = false;
if (!AllowResults) return;
using (BeginDelayedSequence(1000))
{
onCompletionEvent = Schedule(delegate
{
if (!IsCurrentScreen) return;
var score = new Score
{
Beatmap = Beatmap.Value.BeatmapInfo,
Ruleset = ruleset
};
scoreProcessor.PopulateScore(score);
score.User = RulesetContainer.Replay?.User ?? api.LocalUser.Value;
Push(new Results(score));
});
}
}
private bool onFail()
{
if (Beatmap.Value.Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
return false;
HasFailed = true;
return true;
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
Add(RulesetNetworkingClientHandler);
if (!loadedSuccessfully)
return;
Content.Alpha = 0;
Content
.ScaleTo(0.7f)
.ScaleTo(1, 750, Easing.OutQuint)
.Delay(250)
.FadeIn(250);
Task.Run(() =>
{
sourceClock.Reset();
Schedule(() =>
{
adjustableClock.ChangeSource(sourceClock);
applyRateFromMods();
this.Delay(750).Schedule(() =>
{
Logger.Log("Client finnished loading", LoggingTarget.Network, LogLevel.Verbose);
RulesetNetworkingClientHandler.GameLoaded();
});
});
});
}
private void start()
{
adjustableClock.Start();
}
protected override void OnSuspending(Screen next)
{
fadeOut();
base.OnSuspending(next);
}
protected override bool OnExiting(Screen next)
{
Remove(RulesetNetworkingClientHandler);
fadeOut();
return base.OnExiting(next);
}
protected override void UpdateBackgroundElements()
{
if (!IsCurrentScreen) return;
base.UpdateBackgroundElements();
if (ShowStoryboard && storyboard == null)
initializeStoryboard(true);
var beatmap = Beatmap.Value;
var storyboardVisible = ShowStoryboard && beatmap.Storyboard.HasDrawable;
storyboardContainer?
.FadeColour(OsuColour.Gray(BackgroundOpacity), BACKGROUND_FADE_DURATION, Easing.OutQuint)
.FadeTo(storyboardVisible && BackgroundOpacity > 0 ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
if (storyboardVisible && beatmap.Storyboard.ReplacesBackground)
Background?.FadeTo(0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
}
private void fadeOut()
{
const float fade_out_duration = 250;
RulesetContainer?.FadeOut(fade_out_duration);
Content.FadeOut(fade_out_duration);
hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, Easing.In);
Background?.FadeTo(1f, fade_out_duration);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key.Escape)
BackOut();
return base.OnKeyDown(state, args);
}
public void BackOut()
{
RulesetNetworkingClientHandler.AbortGame();
RulesetNetworkingClientHandler.OnAbort();
}
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value;
}
}
@@ -0,0 +1,228 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Screens;
using System;
using osu.Framework.Screens;
using System.Collections.Generic;
using Symcol.Core.Networking;
using Symcol.Rulesets.Core.Multiplayer.Networking;
namespace Symcol.Rulesets.Core.Multiplayer.Screens
{
public abstract class RulesetLobbyScreen : OsuScreen
{
public abstract string RulesetName { get; }
public abstract RulesetMatchScreen MatchScreen { get; }
public RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
public readonly SettingsButton HostGameButton;
public readonly SettingsButton DirectConnectButton;
public readonly SettingsButton JoinGameButton;
public readonly Container NewGame;
protected readonly TextBox HostIP;
protected readonly TextBox HostPort;
//protected readonly TextBox PublicIp;
protected readonly TextBox LocalIp;
public readonly Container JoinIP;
public RulesetLobbyScreen()
{
AlwaysPresent = true;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
HostGameButton = new SettingsButton
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Width = 0.3f,
Text = "Host Game",
Action = HostGame
},
DirectConnectButton = new SettingsButton
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Width = 0.3f,
Text = "Direct Connect",
Action = DirectConnect
},
JoinGameButton = new SettingsButton
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
RelativeSizeAxes = Axes.X,
Width = 0.3f,
Text = "Join Game"
},
NewGame = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Size = new Vector2(400, 300),
Children = new Drawable[]
{
new Box
{
Colour = Color4.Blue,
RelativeSizeAxes = Axes.Both
},
new Box
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = Color4.Black,
Alpha = 0.9f,
RelativeSizeAxes = Axes.X,
Width = 0.48f,
Height = 20,
},
HostIP = new TextBox
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Width = 0.48f,
Height = 20,
Text = "Host IP Address"
},
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = Color4.Black,
Alpha = 0.9f,
RelativeSizeAxes = Axes.X,
Width = 0.48f,
Height = 20,
},
HostPort = new TextBox
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
Width = 0.48f,
Height = 20,
Text = "25570"
},
/*
new Box
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = Color4.Black,
Alpha = 0.9f,
RelativeSizeAxes = Axes.X,
Position = new Vector2(0, 22),
Width = 0.48f,
Height = 20,
},
PublicIp = new TextBox
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Position = new Vector2(0, 22),
Width = 0.48f,
Height = 20,
Text = "You're Public IP Address"
},
*/
new Box
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = Color4.Black,
Alpha = 0.9f,
RelativeSizeAxes = Axes.X,
Position = new Vector2(0, 44),
Width = 0.48f,
Height = 20,
},
LocalIp = new TextBox
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Position = new Vector2(0, 44),
Width = 0.48f,
Height = 20,
Text = "You're Local IP Address"
}
}
}
};
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
MakeCurrent();
}
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
MakeCurrent();
}
protected override bool OnExiting(Screen next)
{
if (RulesetNetworkingClientHandler != null)
{
Remove(RulesetNetworkingClientHandler);
RulesetNetworkingClientHandler.Dispose();
}
return base.OnExiting(next);
}
protected virtual void HostGame()
{
if (RulesetNetworkingClientHandler != null)
{
Remove(RulesetNetworkingClientHandler);
RulesetNetworkingClientHandler.Dispose();
}
Add(RulesetNetworkingClientHandler = new RulesetNetworkingClientHandler(ClientType.Host, LocalIp.Text, Int32.Parse(HostPort.Text)));
List<ClientInfo> list = new List<ClientInfo>();
list.Add(RulesetNetworkingClientHandler.RulesetClientInfo);
JoinMatch(list);
}
protected virtual void DirectConnect()
{
if (RulesetNetworkingClientHandler != null)
{
Remove(RulesetNetworkingClientHandler);
RulesetNetworkingClientHandler.Dispose();
}
Add(RulesetNetworkingClientHandler = new RulesetNetworkingClientHandler(ClientType.Peer, HostIP.Text, Int32.Parse(HostPort.Text), LocalIp.Text));
RulesetNetworkingClientHandler.OnConnectedToHost += (p) => JoinMatch(p);
}
protected virtual void JoinMatch(List<ClientInfo> clientInfos)
{
Remove(RulesetNetworkingClientHandler);
MakeCurrent();
Push(MatchScreen);
}
}
}
@@ -0,0 +1,151 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens;
using osu.Game;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Settings;
using osu.Game.Screens;
using Symcol.Core.Networking;
using Symcol.Rulesets.Core.Multiplayer.Networking;
using Symcol.Rulesets.Core.Multiplayer.Pieces;
using System.Collections.Generic;
namespace Symcol.Rulesets.Core.Multiplayer.Screens
{
public abstract class RulesetMatchScreen : OsuScreen
{
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
private readonly MatchPlayerList playerList;
private BeatmapManager beatmaps;
protected MatchTools MatchTools;
private readonly Chat chat;
public RulesetMatchScreen(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
{
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
Children = new Drawable[]
{
new SettingsButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Width = 0.35f,
Text = "Leave",
Action = () => Exit()
},
new SettingsButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.3f,
Text = "Open Song Select",
Action = () => openSongSelect()
},
new SettingsButton
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.X,
Width = 0.35f,
Text = "Start Match",
Action = () => RulesetNetworkingClientHandler.StartLoadingGame()
},
playerList = new MatchPlayerList(RulesetNetworkingClientHandler),
MatchTools = new MatchTools(),
chat = new Chat(RulesetNetworkingClientHandler)
};
RulesetNetworkingClientHandler.OnPacketReceive += (Packet packet) =>
{
if (packet is RulesetPacket rulesetPacket && rulesetPacket.OnlineBeatmapID != -1)
foreach (BeatmapSetInfo beatmapSet in beatmaps.GetAllUsableBeatmapSets())
if (beatmapSet.OnlineBeatmapSetID == rulesetPacket.OnlineBeatmapSetID)
{
foreach (BeatmapInfo beatmap in beatmapSet.Beatmaps)
if (beatmap.OnlineBeatmapID == rulesetPacket.OnlineBeatmapID)
{
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
Beatmap.Value.Track.Start();
MatchTools.MapChange(Beatmap);
RulesetNetworkingClientHandler.OnMapChange?.Invoke(Beatmap);
break;
}
break;
}
else
MatchTools.MapChange(rulesetPacket.OnlineBeatmapSetID);
};
RulesetNetworkingClientHandler.OnMapChange += (beatmap) => MatchTools.MapChange(beatmap);
}
protected override void LoadComplete()
{
base.LoadComplete();
playerList.Add(RulesetNetworkingClientHandler.RulesetClientInfo);
}
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps)
{
this.beatmaps = beatmaps;
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
MakeCurrent();
Add(RulesetNetworkingClientHandler);
RulesetNetworkingClientHandler.OnLoadGame = (i) => Load(i);
}
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
MakeCurrent();
if (RulesetNetworkingClientHandler != null)
Add(RulesetNetworkingClientHandler);
}
protected override void OnSuspending(Screen next)
{
base.OnSuspending(next);
Remove(RulesetNetworkingClientHandler);
}
protected override bool OnExiting(Screen next)
{
RulesetNetworkingClientHandler.Disconnect();
Remove(RulesetNetworkingClientHandler);
RulesetNetworkingClientHandler.Dispose();
return base.OnExiting(next);
}
protected virtual void Load(List<ClientInfo> playerList)
{
MakeCurrent();
Push(new MultiPlayer(RulesetNetworkingClientHandler, playerList));
}
private void openSongSelect()
{
MatchSongSelect songSelect = new MatchSongSelect(RulesetNetworkingClientHandler);
MakeCurrent();
Push(songSelect);
songSelect.Action = () => RulesetNetworkingClientHandler.SetMap(songSelect.SelectedMap);
}
}
}
@@ -0,0 +1,108 @@
//osu.Game.Screens.Symcol.SymcolMenu
//Symcol.Rulesets.Core.SymcolSettingsSubsection
#define SymcolMods
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Screens;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens;
using osu.Game.Screens.Symcol;
namespace Symcol.Rulesets.Core.Multiplayer.Screens
{
public class RulesetMultiplayerSelection : OsuScreen
{
public static readonly FillFlowContainer<RulesetLobbyItem> LobbyItems = new FillFlowContainer<RulesetLobbyItem>
{
RelativeSizeAxes = Axes.Both,
Width = 0.85f,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
};
public RulesetMultiplayerSelection()
{
RelativeSizeAxes = Axes.Both;
Add(LobbyItems);
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
foreach (RulesetLobbyItem item in LobbyItems)
item.Action = () => Push(item.RulesetLobbyScreen);
}
protected override bool OnExiting(Screen next)
{
Remove(LobbyItems);
#if SymcolMods
SymcolSettingsSubsection.RulesetMultiplayerSelection = new RulesetMultiplayerSelection();
SymcolMenu.RulesetMultiplayerScreen = SymcolSettingsSubsection.RulesetMultiplayerSelection;
#endif
return base.OnExiting(next);
}
}
public abstract class RulesetLobbyItem : ClickableContainer
{
public abstract Texture Icon { get; }
public abstract string RulesetName { get; }
public virtual Texture Background { get; }
public abstract RulesetLobbyScreen RulesetLobbyScreen { get; }
public RulesetLobbyItem()
{
CornerRadius = 20;
Masking = true;
RelativeSizeAxes = Axes.X;
Height = 100;
Children = new Drawable[]
{
new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = Background
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new Sprite
{
Size = new Vector2(Height),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Texture = Icon
},
new OsuSpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Text = RulesetName,
TextSize = 60,
Position = new Vector2(-20, 0)
}
};
}
}
}
@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Symcol.Rulesets.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Symcol.Rulesets.Core")]
[assembly: AssemblyCopyright("Copyright © Shawdooow 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("552b5940-c647-4060-aa4d-61baac415c72")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,97 @@
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
namespace Symcol.Rulesets.Core.Skinning
{
public abstract class SkinElement : Container
{
private static string loadedSkin;
private static ResourceStore<byte[]> skinResources;
private static TextureStore skinTextures;
/// <summary>
/// Will attempt to get a skin element fron the skin, if no element is found return the default element
/// </summary>
/// <param name="stockTextures"></param>
/// <param name="skin"></param>
/// <param name="fileName"></param>
/// <param name="storage"></param>
/// <returns></returns>
public static Texture GetSkinElement(TextureStore stockTextures, Bindable<string> skin, string fileName, Storage storage)
{
Texture texture = null;
string fileNameHd = fileName + "@2x";
Storage skinStorage = storage.GetStorageForDirectory("Skins\\" + skin);
if (skin.Value == "default")
{
texture = stockTextures.Get(fileName + ".png");
if (texture == null)
texture = stockTextures.Get(fileNameHd + ".png");
return texture;
}
if (loadedSkin != skin.ToString())
{
loadedSkin = skin.ToString();
skinResources = new ResourceStore<byte[]>(new StorageBackedResourceStore(skinStorage));
skinTextures = new TextureStore(new RawTextureLoaderStore(skinResources));
}
if (skinStorage.Exists(fileNameHd + ".png"))
texture = skinTextures.Get(fileNameHd + ".png");
else if (skinStorage.Exists(fileName + ".png"))
{
texture = skinTextures.Get(fileName + ".png");
texture.ScaleAdjust = 1f;
}
else
texture = stockTextures.Get(fileNameHd + ".png");
return texture;
}
/// <summary>
/// Will attempt to get a skin element from the skin, if no element is found return null
/// </summary>
/// <param name="skin"></param>
/// <param name="fileName"></param>
/// <param name="storage"></param>
/// <returns></returns>
public static Texture GetElement(Bindable<string> skin, string fileName, Storage storage)
{
Texture texture = null;
string fileNameHd = fileName + "@2x";
Storage skinStorage = storage.GetStorageForDirectory("Skins\\" + skin);
if (loadedSkin != skin.ToString())
{
loadedSkin = skin.ToString();
skinResources = new ResourceStore<byte[]>(new StorageBackedResourceStore(skinStorage));
skinTextures = new TextureStore(new RawTextureLoaderStore(skinResources));
}
if (skinStorage.Exists(fileNameHd + ".png"))
texture = skinTextures.Get(fileNameHd + ".png");
else if (skinStorage.Exists(fileName + ".png"))
{
texture = skinTextures.Get(fileName + ".png");
texture.ScaleAdjust = 1f;
}
else
texture = null;
return texture;
}
}
}
@@ -0,0 +1,25 @@
using osu.Framework.Configuration;
using osu.Framework.Platform;
namespace Symcol.Rulesets.Core.Skinning
{
public class SkinConfigReader<T> : IniConfigManager<T>
where T : struct
{
protected override string Filename => @"skin.ini";
public SkinConfigReader(Storage storage) : base(storage) { }
protected override bool PerformSave() { return false; }
}
//wildly incomplete
public enum ClassicIniParameters
{
Name,
Author,
CursorRotate,
CursorExpand,
CursorCentre
}
}
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{552B5940-C647-4060-AA4D-61BAAC415C72}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Symcol.Rulesets.Core</RootNamespace>
<AssemblyName>Symcol.Rulesets.Core</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Containers\LinkText.cs" />
<Compile Include="Containers\ProfileLink.cs" />
<Compile Include="HitObjects\DrawableSymcolHitObject.cs" />
<Compile Include="Multiplayer\Networking\ScorePacket.cs" />
<Compile Include="Multiplayer\Pieces\Chat.cs" />
<Compile Include="Multiplayer\Pieces\ChatMessage.cs" />
<Compile Include="Multiplayer\Networking\ChatPacket.cs" />
<Compile Include="Multiplayer\Pieces\MatchTools.cs" />
<Compile Include="Multiplayer\Networking\RulesetClientInfo.cs" />
<Compile Include="Multiplayer\Pieces\MatchPlayer.cs" />
<Compile Include="Multiplayer\Pieces\MatchPlayerList.cs" />
<Compile Include="Multiplayer\Options\MultiplayerDropdownEnumOption.cs" />
<Compile Include="Multiplayer\Options\MultiplayerOption.cs" />
<Compile Include="Multiplayer\Options\MultiplayerToggleOption.cs" />
<Compile Include="Multiplayer\Pieces\MultiplayerScoreboard.cs" />
<Compile Include="Multiplayer\Pieces\MultiplayerScoreboardItem.cs" />
<Compile Include="Multiplayer\Screens\MatchSongSelect.cs" />
<Compile Include="Multiplayer\Screens\MultiPlayer.cs" />
<Compile Include="Multiplayer\Screens\RulesetLobbyScreen.cs" />
<Compile Include="Multiplayer\Screens\RulesetMatchScreen.cs" />
<Compile Include="Multiplayer\Screens\RulesetMultiplayerSelection.cs" />
<Compile Include="Multiplayer\Networking\RulesetNetworkingClientHandler.cs" />
<Compile Include="Multiplayer\Networking\RulesetPacket.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Skinning\SkinElement.cs" />
<Compile Include="Skinning\SkinIniReader.cs" />
<Compile Include="SymcolConfigManager.cs" />
<Compile Include="SymcolPlayfield.cs" />
<Compile Include="SymcolSettingsSubsection.cs" />
<Compile Include="SymcolInputManager.cs" />
<Compile Include="VectorVideos\VectorVideo.cs" />
<Compile Include="Wiki\WikiOptionEnumExplanation.cs" />
<Compile Include="Wiki\WikiParagraph.cs" />
<Compile Include="Wiki\WikiSubSectionHeader.cs" />
<Compile Include="Wiki\WikiOverlay.cs" />
<Compile Include="Wiki\WikiHeader.cs" />
<Compile Include="Wiki\WikiSection.cs" />
<Compile Include="Wiki\WikiSubSectionLinkHeader.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-Framework\osu.Framework\osu.Framework.csproj">
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{d9a367c9-4c1a-489f-9b05-a0cea2b53b58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\Symcol.Core\Symcol.Core.csproj">
<Project>{F34AC16C-E590-4D70-A069-A748326852BF}</Project>
<Name>Symcol.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,22 @@
using osu.Framework.Configuration;
using osu.Framework.Platform;
namespace Symcol.Rulesets.Core
{
public class SymcolConfigManager : IniConfigManager<SymcolSetting>
{
protected override string Filename => "symcol.ini";
public SymcolConfigManager(Storage storage) : base(storage) { }
protected override void InitialiseDefaults()
{
Set(SymcolSetting.PlayerColor, "#ffffff");
}
}
public enum SymcolSetting
{
PlayerColor
}
}
@@ -0,0 +1,19 @@
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets;
using osu.Game.Rulesets.UI;
using Symcol.Rulesets.Core.VectorVideos;
namespace Symcol.Rulesets.Core
{
public class SymcolInputManager<T> : RulesetInputManager<T>
where T : struct
{
protected virtual bool VectorVideo => false;
public SymcolInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
{
Child = new VectorVideo();
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using OpenTK;
using osu.Game.Rulesets.UI;
using Symcol.Rulesets.Core.Multiplayer.Networking;
namespace Symcol.Rulesets.Core
{
public class SymcolPlayfield : Playfield
{
public static RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
public SymcolPlayfield(Vector2 size) : base(size.X)
{
}
}
}
@@ -0,0 +1,65 @@
//osu.Game.Screens.Symcol.SymcolMenu
//Symcol.Rulesets.Core.Multiplayer.Screens.RulesetMultiplayerSelection
#define SymcolMods
using osu.Framework.Allocation;
using osu.Game;
using osu.Game.Overlays.Settings;
using Symcol.Rulesets.Core.Wiki;
using osu.Game.Screens.Symcol;
using Symcol.Rulesets.Core.Multiplayer.Screens;
using osu.Framework.Platform;
using osu.Framework.Logging;
namespace Symcol.Rulesets.Core
{
public abstract class SymcolSettingsSubsection : SettingsSubsection
{
public virtual WikiOverlay Wiki => null;
public virtual RulesetLobbyItem RulesetLobbyItem => null;
#if SymcolMods
public static RulesetMultiplayerSelection RulesetMultiplayerSelection;
#endif
public static SymcolConfigManager SymcolConfigManager;
private OsuGame osu;
public SymcolSettingsSubsection()
{
#if SymcolMods
if (RulesetLobbyItem != null)
RulesetMultiplayerSelection.LobbyItems.Add(RulesetLobbyItem);
if (RulesetMultiplayerSelection == null)
RulesetMultiplayerSelection = new RulesetMultiplayerSelection();
SymcolMenu.RulesetMultiplayerScreen = RulesetMultiplayerSelection;
#endif
#if !SymcolMods
Logger.Log("osu.Game mods not installed! Online Multiplayer will not be avalible without them. . .", LoggingTarget.Information, LogLevel.Important);
#endif
}
[BackgroundDependencyLoader]
private void load(OsuGame osu, Storage storage)
{
this.osu = osu;
if (SymcolConfigManager == null)
SymcolConfigManager = new SymcolConfigManager(storage);
}
protected override void LoadComplete()
{
base.LoadComplete();
if (Wiki != null)
osu.Add(Wiki);
}
}
}
@@ -0,0 +1,59 @@
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Graphics.Containers;
namespace Symcol.Rulesets.Core.VectorVideos
{
public class VectorVideo : BeatSyncedContainer
{
public const string FILE_NAME = "VectorVideo.symcol";
[BackgroundDependencyLoader]
private void load(Storage storage)
{
}
protected void LoadContent(string args)
{
string[] parameters = args.Split(',');
ObjectType objectType = ObjectType.LogoVisualizer;
Anchor anchor = Anchor.Centre;
Anchor origin = Anchor.Centre;
bool checkingType = false;
foreach (string parameter in parameters)
{
string[] subParameters = parameter.Split('=');
foreach (string subParameter in subParameters)
{
if (subParameter == "Type")
checkingType = true;
if (checkingType)
switch (subParameter)
{
case "LogoVisualizer":
objectType = ObjectType.LogoVisualizer;
break;
}
}
}
}
private void loadLogoVisualizer()
{
}
}
public enum ObjectType
{
LogoVisualizer
}
}
+114
View File
@@ -0,0 +1,114 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Users;
using osu.Game.Graphics.Containers;
using OpenTK;
using osu.Framework.Graphics.Shapes;
using OpenTK.Graphics;
using Symcol.Rulesets.Core.Containers;
namespace Symcol.Rulesets.Core.Wiki
{
public abstract class WikiHeader : Container
{
protected abstract Texture RulesetIcon { get; }
protected abstract string RulesetName { get; }
protected abstract string RulesetDescription { get; }
protected virtual string RulesetUrl => $@"https://osu.ppy.sh/home";
protected virtual User Creator => null;
protected virtual User Maintainer => null;
protected virtual string DiscordInvite => $@"https://discord.gg/ppy";
protected virtual Texture HeaderBackground => null;
private const float description_height = 150;
private const float description_width = 220;
private const float icon_size = 200;
private const float header_margin = 50;
private const float rulesetname_height = 60;
public WikiHeader()
{
Masking = true;
RelativeSizeAxes = Axes.X;
Height = header_margin + icon_size + rulesetname_height;
var user = Creator;
bool maintainer = false;
string userTitle = "Creator";
if (Creator == null)
{
user = Maintainer;
maintainer = true;
userTitle = "Maintainer";
}
Children = new Drawable[]
{
new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = HeaderBackground
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new Sprite
{
Size = new Vector2(icon_size),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Texture = RulesetIcon
},
new LinkText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, icon_size),
Text = RulesetName,
Url = RulesetUrl,
Font = @"Exo2.0-RegularItalic",
TextSize = rulesetname_height
},
new ProfileLink(user, maintainer)
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, icon_size + rulesetname_height),
},
new LinkText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Position = new Vector2(10, icon_size + rulesetname_height + 20),
Text = userTitle + "'s Discord server",
Url = DiscordInvite,
Font = @"Exo2.0-RegularItalic",
TextSize = 16
},
new OsuTextFlowContainer(t => { t.TextSize = 20; })
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Size = new Vector2(description_width, description_height),
Text = RulesetDescription
}
};
}
}
}
@@ -0,0 +1,78 @@
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Settings;
namespace Symcol.Rulesets.Core.Wiki
{
public class WikiOptionEnumExplanation<T> : Container
where T : struct
{
public OsuTextFlowContainer Description;
public WikiOptionEnumExplanation(Bindable<T> bindable)
{
OsuColour osu = new OsuColour();
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Masking = true;
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = osu.Yellow,
Masking = true,
RelativeSizeAxes = Axes.Y,
Size = new Vector2(10, 0.98f),
CornerRadius = 5,
Child = new Box
{
RelativeSizeAxes = Axes.Both
}
},
new Container
{
Position = new Vector2(-10, 0),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0.45f,
Child = new SettingsEnumDropdown<T>
{
Bindable = bindable
}
},
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
Width = 0.45f,
AutoSizeAxes = Axes.Y,
AutoSizeDuration = 100,
AutoSizeEasing = Easing.OutQuint,
Child = Description = new OsuTextFlowContainer(t => { t.TextSize = 20; })
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
}
}
};
}
}
}
+147
View File
@@ -0,0 +1,147 @@
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using System.Linq;
namespace Symcol.Rulesets.Core.Wiki
{
public abstract class WikiOverlay : WaveOverlayContainer
{
protected abstract WikiHeader Header { get; }
protected abstract WikiSection[] Sections { get; }
private WikiSection lastSection;
private SectionsContainer<WikiSection> sectionsContainer;
private WikiTabControl tabs;
public const float CONTENT_X_MARGIN = 100;
public WikiOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
Width = 0.85f;
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Masking = true;
AlwaysPresent = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0),
Type = EdgeEffectType.Shadow,
Radius = 10
};
tabs = new WikiTabControl
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 30
};
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
});
Add(sectionsContainer = new SectionsContainer<WikiSection>
{
RelativeSizeAxes = Axes.Both,
ExpandableHeader = Header,
FixedHeader = tabs,
HeaderBackground = new Box
{
Colour = OsuColour.Gray(34),
RelativeSizeAxes = Axes.Both
}
});
sectionsContainer.SelectedSection.ValueChanged += s =>
{
if (lastSection != s)
{
lastSection = s;
tabs.Current.Value = lastSection;
}
};
tabs.Current.ValueChanged += s =>
{
if (lastSection == null)
{
lastSection = sectionsContainer.Children.FirstOrDefault();
if (lastSection != null)
tabs.Current.Value = lastSection;
return;
}
if (lastSection != s)
{
lastSection = s;
sectionsContainer.ScrollTo(lastSection);
}
};
foreach (WikiSection sec in Sections)
{
if (sec != null)
{
sectionsContainer.Add(sec);
tabs.AddItem(sec);
}
}
sectionsContainer.ScrollToTop();
}
protected override void PopIn()
{
base.PopIn();
FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
private class WikiTabControl : PageTabControl<WikiSection>
{
public WikiTabControl()
{
TabContainer.RelativeSizeAxes &= ~Axes.X;
TabContainer.AutoSizeAxes |= Axes.X;
TabContainer.Anchor |= Anchor.x1;
TabContainer.Origin |= Anchor.x1;
}
protected override TabItem<WikiSection> CreateTabItem(WikiSection value) => new WikiTabItem(value);
protected override Dropdown<WikiSection> CreateDropdown() => null;
private class WikiTabItem : PageTabItem
{
public WikiTabItem(WikiSection value) : base(value)
{
Text.Text = value.Title;
}
}
}
}
}
@@ -0,0 +1,63 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
namespace Symcol.Rulesets.Core.Wiki
{
public class WikiParagraph : Container
{
public WikiParagraph(string text, float textsize = 20)
{
paragraphNoMarkdown(text, textsize);
}
public WikiParagraph(string text, bool markdown)
{
if (!markdown)
paragraphNoMarkdown(text, 20);
else
paragraphMarkdown(text, 20);
}
public WikiParagraph(string text, float textsize, bool markdown)
{
if (!markdown)
paragraphNoMarkdown(text, textsize);
else
paragraphMarkdown(text, textsize);
}
private void paragraphNoMarkdown(string text, float textsize)
{
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Child = new OsuTextFlowContainer(t => { t.TextSize = textsize; })
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Text = text
};
}
private void paragraphMarkdown(string text, float textsize)
{
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Child = new OsuTextFlowContainer(t => { t.TextSize = textsize; })
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Text = text
};
}
}
}
+59
View File
@@ -0,0 +1,59 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace Symcol.Rulesets.Core.Wiki
{
public abstract class WikiSection : FillFlowContainer
{
public abstract string Title { get; }
private readonly FillFlowContainer content;
protected override Container<Drawable> Content => content;
protected WikiSection()
{
OsuColour osu = new OsuColour();
Direction = FillDirection.Vertical;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new OsuSpriteText
{
Colour = osu.Yellow,
Text = Title,
TextSize = 32,
Font = @"Exo2.0-Bold",
Margin = new MarginPadding
{
Horizontal = WikiOverlay.CONTENT_X_MARGIN,
Vertical = 12
}
},
content = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
{
Horizontal = WikiOverlay.CONTENT_X_MARGIN,
Bottom = 20
}
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(34),
EdgeSmoothness = new Vector2(1)
}
};
}
}
}
@@ -0,0 +1,22 @@
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace Symcol.Rulesets.Core.Wiki
{
public class WikiSubSectionHeader : OsuSpriteText
{
public WikiSubSectionHeader(string text)
{
OsuColour osu = new OsuColour();
Colour = osu.Pink;
Text = text;
TextSize = 24;
Font = @"Exo2.0-BoldItalic";
Margin = new MarginPadding
{
Vertical = 10
};
}
}
}
@@ -0,0 +1,28 @@
using osu.Framework.Graphics;
using osu.Game.Graphics;
using Symcol.Rulesets.Core.Containers;
namespace Symcol.Rulesets.Core.Wiki
{
public class WikiSubSectionLinkHeader : LinkText
{
public override string Tooltip => tooltip;
private string tooltip = "";
public WikiSubSectionLinkHeader(string text, string url, string tooltip = "")
{
this.tooltip = tooltip;
Url = url;
OsuColour osu = new OsuColour();
Colour = osu.Pink;
Text = text;
TextSize = 24;
Font = @"Exo2.0-BoldItalic";
Margin = new MarginPadding
{
Vertical = 10
};
}
}
}
+28 -25
View File
@@ -1,26 +1,29 @@
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017 preview
configuration: Debug
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
- inspectcode -> appveyor.yml
- packages -> **\packages.config
install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
- cmd: choco install nvika -y
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.5/CodeFileSanity.exe
before_build:
- cmd: CodeFileSanity.exe
- cmd: nuget restore -verbosity quiet
environment:
TargetFramework: net471
build:
project: osu.sln
parallel: true
verbosity: minimal
after_build:
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
# 2017-09-14
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
configuration: Debug
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
- inspectcode -> appveyor.yml
- packages -> **\packages.config
install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
- cmd: choco install nvika -y
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe
before_build:
- cmd: CodeFileSanity.exe
- cmd: nuget restore -verbosity quiet
build:
project: osu.sln
parallel: true
verbosity: minimal
test:
assemblies:
only:
- 'osu.Desktop\**\*.dll'
after_build:
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
-34
View File
@@ -1,34 +0,0 @@
clone_depth: 1
version: '{build}'
# skip_non_tags: true
image: Visual Studio 2017
cache:
- '%USERPROFILE%\.nuget\packages -> **\*.csproj'
install:
- git clone https://github.com/ppy/osu-deploy
before_build:
- ps: if($env:appveyor_repo_tag -eq 'True') { Update-AppveyorBuild -Version $env:appveyor_repo_tag_name }
- cmd: git submodule update --init --recursive --depth=5
- cmd: nuget restore -verbosity quiet
build_script:
- ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
- appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate
- cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
- appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
- cd osu-deploy
- nuget restore -verbosity quiet
- msbuild osu.Desktop.Deploy.csproj
- cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\net471\osu-deploy.exe.config
- cd bin\Debug\net471\
- osu.Desktop.Deploy.exe %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
environment:
TargetFramework: net471
decode_secret:
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
code_signing_password:
secure: 34tLNqvjmmZEi97MLKfrnQ==
artifacts:
- path: 'Releases\*'
deploy:
- provider: Environment
name: github
-29
View File
@@ -1,29 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [{
"name": "Deploy (Debug)",
"request": "launch",
"type": "mono",
"program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "Deploy (Release)",
"request": "launch",
"type": "clr",
"program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
}
]
}
-64
View File
@@ -1,64 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"command": "msbuild",
"type": "shell",
"suppressTaskName": true,
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/verbosity:minimal",
"/m" //parallel compiling support.
],
"tasks": [{
"taskName": "Build (Debug)",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$msCompile"
]
},
{
"taskName": "Build (Release)",
"group": "build",
"args": [
"/property:Configuration=Release"
],
"problemMatcher": [
"$msCompile"
]
},
{
"taskName": "Clean (Debug)",
"args": [
"/target:Clean"
],
"problemMatcher": [
"$msCompile"
]
},
{
"taskName": "Clean (Release)",
"args": [
"/target:Clean",
"/property:Configuration=Release"
],
"problemMatcher": [
"$msCompile"
]
},
{
"taskName": "Clean All",
"dependsOn": [
"Clean (Debug)",
"Clean (Release)"
],
"problemMatcher": [
"$msCompile"
]
}
]
}
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<configuration>
<appSettings>
<add key="StagingFolder" value="Staging" />
<add key="ReleasesFolder" value="Releases" />
<add key="GitHubAccessToken" value="" />
<add key="GitHubUsername" value="ppy" />
<add key="GitHubRepoName" value="osu" />
<add key="ProjectName" value="osu.Desktop" />
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
<add key="SolutionName" value="osu" />
<add key="TargetName" value="osu.Desktop" />
<add key="PackageName" value="osulazer" />
<add key="IconName" value="lazer.ico" />
<add key="CodeSigningCertificate" value="" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Desktop.Deploy
{
public class GitHubObject
{
[JsonProperty(@"id")]
public int Id;
[JsonProperty(@"name")]
public string Name;
}
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Desktop.Deploy
{
public class GitHubRelease
{
[JsonProperty(@"id")]
public int Id;
[JsonProperty(@"tag_name")]
public string TagName => $"v{Name}";
[JsonProperty(@"name")]
public string Name;
[JsonProperty(@"draft")]
public bool Draft;
[JsonProperty(@"prerelease")]
public bool PreRelease;
[JsonProperty(@"upload_url")]
public string UploadUrl;
}
}
+431
View File
@@ -0,0 +1,431 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.IO.Network;
using FileWebRequest = osu.Framework.IO.Network.FileWebRequest;
using WebRequest = osu.Framework.IO.Network.WebRequest;
namespace osu.Desktop.Deploy
{
internal static class Program
{
private const string nuget_path = @"packages\NuGet.CommandLine.4.3.0\tools\NuGet.exe";
private const string squirrel_path = @"packages\squirrel.windows.1.7.8\tools\Squirrel.exe";
private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe";
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"];
public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"];
public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"];
public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"];
public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"];
public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"];
public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"];
public static string TargetNames = ConfigurationManager.AppSettings["TargetName"];
public static string PackageName = ConfigurationManager.AppSettings["PackageName"];
public static string IconName = ConfigurationManager.AppSettings["IconName"];
public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"];
public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases";
public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases";
/// <summary>
/// How many previous build deltas we want to keep when publishing.
/// </summary>
private const int keep_delta_count = 3;
private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";
private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate);
private static string solutionPath => Environment.CurrentDirectory;
private static string stagingPath => Path.Combine(solutionPath, StagingFolder);
private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName);
private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg";
private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg";
private static readonly Stopwatch sw = new Stopwatch();
private static string codeSigningPassword;
public static void Main(string[] args)
{
displayHeader();
findSolutionPath();
if (!Directory.Exists(ReleasesFolder))
{
write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow);
Directory.CreateDirectory(ReleasesFolder);
}
checkGitHubReleases();
refreshDirectory(StagingFolder);
//increment build number until we have a unique one.
string verBase = DateTime.Now.ToString("yyyy.Mdd.");
int increment = 0;
while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any())
increment++;
string version = $"{verBase}{increment}";
Console.ForegroundColor = ConsoleColor.White;
Console.Write($"Ready to deploy {version}: ");
Console.ReadLine();
sw.Start();
if (!string.IsNullOrEmpty(CodeSigningCertificate))
{
Console.Write("Enter code signing password: ");
codeSigningPassword = readLineMasked();
}
write("Restoring NuGet packages...");
runCommand(nuget_path, "restore " + solutionPath);
write("Updating AssemblyInfo...");
updateAssemblyInfo(version);
write("Running build process...");
foreach (string targetName in TargetNames.Split(','))
runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
write("Creating NuGet deployment package...");
runCommand(nuget_path, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");
//prune once before checking for files so we can avoid erroring on files which aren't even needed for this build.
pruneReleases();
checkReleaseFiles();
write("Running squirrel build...");
runCommand(squirrel_path, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
//prune again to clean up before upload.
pruneReleases();
//rename setup to install.
File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true);
File.Delete(Path.Combine(ReleasesFolder, "Setup.exe"));
uploadBuild(version);
//reset assemblyinfo.
updateAssemblyInfo("0.0.0");
write("Done!", ConsoleColor.White);
Console.ReadLine();
}
private static void displayHeader()
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine();
Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law.");
Console.WriteLine(" Do not distribute builds of this project publicly that make use of these.");
Console.ResetColor();
Console.WriteLine();
}
/// <summary>
/// Ensure we have all the files in the release directory which are expected to be there.
/// This should have been accounted for in earlier steps, and just serves as a verification step.
/// </summary>
private static void checkReleaseFiles()
{
if (!canGitHub) return;
var releaseLines = getReleaseLines();
//ensure we have all files necessary
foreach (var l in releaseLines)
if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename)))
error($"Local file missing {l.Filename}");
}
private static IEnumerable<ReleaseLine> getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l));
private static void pruneReleases()
{
if (!canGitHub) return;
write("Pruning RELEASES...");
var releaseLines = getReleaseLines().ToList();
var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1);
//remove any FULL releases (except most recent)
foreach (var l in fulls)
{
write($"- Removing old release {l.Filename}", ConsoleColor.Yellow);
File.Delete(Path.Combine(ReleasesFolder, l.Filename));
releaseLines.Remove(l);
}
//remove excess deltas
var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray();
if (deltas.Length > keep_delta_count)
{
foreach (var l in deltas.Take(deltas.Length - keep_delta_count))
{
write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow);
File.Delete(Path.Combine(ReleasesFolder, l.Filename));
releaseLines.Remove(l);
}
}
var lines = new List<string>();
releaseLines.ForEach(l => lines.Add(l.ToString()));
File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines);
}
private static void uploadBuild(string version)
{
if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
return;
write("Publishing to GitHub...");
write($"- Creating release {version}...", ConsoleColor.Yellow);
var req = new JsonWebRequest<GitHubRelease>($"{GitHubApiEndpoint}")
{
Method = HttpMethod.POST,
};
req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease
{
Name = version,
Draft = true,
PreRelease = true
}));
req.AuthenticatedBlockingPerform();
var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}");
foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first.
{
write($"- Adding asset {a}...", ConsoleColor.Yellow);
var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a))
{
Method = HttpMethod.POST,
Timeout = 240000,
ContentType = "application/octet-stream",
};
upload.AddRaw(File.ReadAllBytes(a));
upload.AuthenticatedBlockingPerform();
}
openGitHubReleasePage();
}
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
private static void checkGitHubReleases()
{
if (!canGitHub) return;
write("Checking GitHub releases...");
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
req.AuthenticatedBlockingPerform();
var lastRelease = req.ResponseObject.FirstOrDefault();
if (lastRelease == null)
return;
if (lastRelease.Draft)
{
openGitHubReleasePage();
error("There's a pending draft release! You probably don't want to push a build with this present.");
}
//there's a previous release for this project.
var assetReq = new JsonWebRequest<List<GitHubObject>>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets");
assetReq.AuthenticatedBlockingPerform();
var assets = assetReq.ResponseObject;
//make sure our RELEASES file is the same as the last build on the server.
var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES");
//if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one.
if (releaseAsset == null) return;
write($"Last GitHub release was {lastRelease.Name}.");
bool requireDownload = false;
if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name))))
{
write("Last version's package not found locally.", ConsoleColor.Red);
requireDownload = true;
}
else
{
var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}");
lastReleases.AuthenticatedBlockingPerform();
if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString)
{
write("Server's RELEASES differed from ours.", ConsoleColor.Red);
requireDownload = true;
}
}
if (!requireDownload) return;
write("Refreshing local releases directory...");
refreshDirectory(ReleasesFolder);
foreach (var a in assets)
{
if (a.Name.EndsWith(".exe")) continue;
write($"- Downloading {a.Name}...", ConsoleColor.Yellow);
new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform();
}
}
private static void refreshDirectory(string directory)
{
if (Directory.Exists(directory))
Directory.Delete(directory, true);
Directory.CreateDirectory(directory);
}
private static void updateAssemblyInfo(string version)
{
string file = Path.Combine(ProjectName, "Properties", "AssemblyInfo.cs");
var l1 = File.ReadAllLines(file);
List<string> l2 = new List<string>();
foreach (var l in l1)
{
if (l.StartsWith("[assembly: AssemblyVersion("))
l2.Add($"[assembly: AssemblyVersion(\"{version}\")]");
else if (l.StartsWith("[assembly: AssemblyFileVersion("))
l2.Add($"[assembly: AssemblyFileVersion(\"{version}\")]");
else
l2.Add(l);
}
File.WriteAllLines(file, l2);
}
/// <summary>
/// Find the base path of the active solution (git checkout location)
/// </summary>
private static void findSolutionPath()
{
string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim());
if (string.IsNullOrEmpty(path))
path = Environment.CurrentDirectory;
while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln")))
path = path.Remove(path.LastIndexOf('\\'));
path += "\\";
Environment.CurrentDirectory = path;
}
private static bool runCommand(string command, string args)
{
var psi = new ProcessStartInfo(command, args)
{
WorkingDirectory = solutionPath,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
Process p = Process.Start(psi);
if (p == null) return false;
string output = p.StandardOutput.ReadToEnd();
output += p.StandardError.ReadToEnd();
if (p.ExitCode == 0) return true;
write(output);
error($"Command {command} {args} failed!");
return false;
}
private static string readLineMasked()
{
var fg = Console.ForegroundColor;
Console.ForegroundColor = Console.BackgroundColor;
var ret = Console.ReadLine();
Console.ForegroundColor = fg;
return ret;
}
private static void error(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"FATAL ERROR: {message}");
Console.ReadLine();
Environment.Exit(-1);
}
private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
{
if (sw.ElapsedMilliseconds > 0)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8));
}
Console.ForegroundColor = col;
Console.WriteLine(message);
}
public static void AuthenticatedBlockingPerform(this WebRequest r)
{
r.AddHeader("Authorization", $"token {GitHubAccessToken}");
r.Perform();
}
}
internal class RawFileWebRequest : WebRequest
{
public RawFileWebRequest(string url) : base(url)
{
}
protected override string Accept => "application/octet-stream";
}
internal class ReleaseLine
{
public string Hash;
public string Filename;
public int Filesize;
public ReleaseLine(string line)
{
var split = line.Split(' ');
Hash = split[0];
Filename = split[1];
Filesize = int.Parse(split[2]);
}
public override string ToString() => $"{Hash} {Filename} {Filesize}";
}
}
@@ -0,0 +1,38 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("osu.Desktop.Deploy")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("osu.Desktop.Deploy")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("baea2f74-0315-4667-84e0-acac0b4bf785")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Game.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Deploy</RootNamespace>
<AssemblyName>osu.Desktop.Deploy</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>osu.Desktop.Deploy.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.MsDelta, Version=1.1.0.0, Culture=neutral, PublicKeyToken=46b2138a390abf55, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.MsDelta.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.PatchApi, Version=1.1.0.0, Culture=neutral, PublicKeyToken=3e8888ee913ed789, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Squirrel, Version=1.7.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="GitHubObject.cs" />
<Compile Include="GitHubRelease.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
<Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net452" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
<package id="NuGet.CommandLine" version="4.3.0" targetFramework="net461" developmentDependency="true" />
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
<package id="Splat" version="2.0.0" targetFramework="net452" />
<package id="squirrel.windows" version="1.7.8" targetFramework="net461" />
</packages>
+25
View File
@@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>
+121 -121
View File
@@ -1,121 +1,121 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using osu.Desktop.Overlays;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game;
using OpenTK.Input;
using Microsoft.Win32;
using osu.Framework.Platform.Windows;
namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
private readonly bool noVersionOverlay;
public OsuGameDesktop(string[] args = null)
: base(args)
{
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
}
public override Storage GetStorageForStableInstall()
{
try
{
return new StableStorage();
}
catch
{
return null;
}
}
/// <summary>
/// A method of accessing an osu-stable install in a controlled fashion.
/// </summary>
private class StableStorage : WindowsStorage
{
protected override string LocateBasePath()
{
bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
string stableInstallPath;
try
{
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
if (checkExists(stableInstallPath))
return stableInstallPath;
}
catch
{
}
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
if (checkExists(stableInstallPath))
return stableInstallPath;
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
if (checkExists(stableInstallPath))
return stableInstallPath;
return null;
}
public StableStorage()
: base(string.Empty)
{
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!noVersionOverlay)
{
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.State = Visibility.Visible;
});
}
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null)
{
desktopWindow.CursorState |= CursorState.Hidden;
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
desktopWindow.Title = Name;
desktopWindow.FileDrop += fileDrop;
}
}
private void fileDrop(object sender, FileDropEventArgs e)
{
var filePaths = new[] { e.FileName };
var firstExtension = Path.GetExtension(filePaths.First());
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Win32;
using osu.Desktop.Overlays;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game;
using OpenTK.Input;
namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
private readonly bool noVersionOverlay;
public OsuGameDesktop(string[] args = null)
: base(args)
{
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
}
public override Storage GetStorageForStableInstall()
{
try
{
return new StableStorage();
}
catch
{
return null;
}
}
/// <summary>
/// A method of accessing an osu-stable install in a controlled fashion.
/// </summary>
private class StableStorage : DesktopStorage
{
protected override string LocateBasePath()
{
bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
string stableInstallPath;
try
{
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
if (checkExists(stableInstallPath))
return stableInstallPath;
}
catch
{
}
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
if (checkExists(stableInstallPath))
return stableInstallPath;
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
if (checkExists(stableInstallPath))
return stableInstallPath;
return null;
}
public StableStorage()
: base(string.Empty)
{
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!noVersionOverlay)
{
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.State = Visibility.Visible;
});
}
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null)
{
desktopWindow.CursorState |= CursorState.Hidden;
desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
desktopWindow.Title = Name;
desktopWindow.FileDrop += fileDrop;
}
}
private void fileDrop(object sender, FileDropEventArgs e)
{
var filePaths = new [] { e.FileName };
var firstExtension = Path.GetExtension(filePaths.First());
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
}
}
}
@@ -1,29 +1,30 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Screens.Backgrounds;
namespace osu.Game.Tests
{
public class OsuTestBrowser : OsuGameBase
{
protected override void LoadComplete()
{
base.LoadComplete();
LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal);
// Have to construct this here, rather than in the constructor, because
// we depend on some dependencies to be loaded within OsuGameBase.load().
Add(new TestBrowser());
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
host.Window.CursorState |= CursorState.Hidden;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game;
using osu.Game.Screens.Backgrounds;
namespace osu.Desktop
{
internal class OsuTestBrowser : OsuGameBase
{
protected override void LoadComplete()
{
base.LoadComplete();
LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal);
// Have to construct this here, rather than in the constructor, because
// we depend on some dependencies to be loaded within OsuGameBase.load().
Add(new TestBrowser());
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
host.Window.CursorState |= CursorState.Hidden;
}
}
}
@@ -1,164 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
#if NET_FRAMEWORK
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Game;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using OpenTK;
using OpenTK.Graphics;
using Squirrel;
namespace osu.Desktop.Overlays
{
public class SquirrelUpdateManager : Component
{
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
public void PrepareUpdate()
{
// Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
UpdateManager.RestartAppWhenExited().Wait();
}
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuGameBase game)
{
notificationOverlay = notification;
if (game.IsDeployedBuild)
Schedule(() => checkForUpdateAsync());
}
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
{
//should we schedule a retry on completion of this check?
bool scheduleRetry = true;
try
{
if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true);
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
if (info.ReleasesToApply.Count == 0)
//no updates available. bail and retry later.
return;
if (notification == null)
{
notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active };
Schedule(() => notificationOverlay.Post(notification));
}
notification.Progress = 0;
notification.Text = @"Downloading update...";
try
{
await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f);
notification.Progress = 0;
notification.Text = @"Installing update...";
await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f);
notification.State = ProgressNotificationState.Completed;
}
catch (Exception e)
{
if (useDeltaPatching)
{
Logger.Error(e, @"delta patching failed!");
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
//try again without deltas.
checkForUpdateAsync(false, notification);
scheduleRetry = false;
}
else
{
Logger.Error(e, @"update failed!");
}
}
}
catch (Exception)
{
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
}
finally
{
if (scheduleRetry)
{
if (notification != null)
notification.State = ProgressNotificationState.Cancelled;
//check again in 30 minutes.
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
}
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
updateManager?.Dispose();
}
private class UpdateProgressNotification : ProgressNotification
{
private readonly SquirrelUpdateManager updateManager;
private OsuGame game;
public UpdateProgressNotification(SquirrelUpdateManager updateManager)
{
this.updateManager = updateManager;
}
protected override Notification CreateCompletionNotification()
{
return new ProgressCompletionNotification
{
Text = @"Update ready to install. Click to restart!",
Activated = () =>
{
updateManager.PrepareUpdate();
game.GracefullyExit();
return true;
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, OsuGame game)
{
this.game = game;
IconContent.AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
},
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_upload,
Colour = Color4.White,
Size = new Vector2(20),
}
});
}
}
}
}
#endif
+266 -142
View File
@@ -1,142 +1,266 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Desktop.Overlays
{
public class VersionManager : OverlayContainer
{
private OsuConfigManager config;
private OsuGameBase game;
private NotificationOverlay notificationOverlay;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
Alpha = 0;
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = game.Name
},
new OsuSpriteText
{
Colour = DebugUtils.IsDebug ? colours.Red : Color4.White,
Text = game.Version
},
}
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TextSize = 12,
Colour = colours.Yellow,
Font = @"Venera",
Text = @"Development Build"
},
new Sprite
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
},
}
}
};
#if NET_FRAMEWORK
Add(new SquirrelUpdateManager());
#endif
}
protected override void LoadComplete()
{
base.LoadComplete();
var version = game.Version;
var lastVersion = config.Get<string>(OsuSetting.Version);
if (game.IsDeployedBuild && version != lastVersion)
{
config.Set(OsuSetting.Version, version);
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
notificationOverlay.Post(new UpdateCompleteNotification(version));
}
}
private class UpdateCompleteNotification : SimpleNotification
{
public UpdateCompleteNotification(string version)
{
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
return true;
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IconBackgound.Colour = colours.BlueDark;
}
}
protected override void PopIn()
{
this.FadeIn(1000);
}
protected override void PopOut()
{
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Game;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using OpenTK;
using OpenTK.Graphics;
using Squirrel;
namespace osu.Desktop.Overlays
{
public class VersionManager : OverlayContainer
{
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
private OsuConfigManager config;
private OsuGameBase game;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
Alpha = 0;
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = game.Name
},
new OsuSpriteText
{
Colour = DebugUtils.IsDebug ? colours.Red : Color4.White,
Text = game.Version
},
}
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TextSize = 12,
Colour = colours.Yellow,
Font = @"Venera",
Text = @"Development Build"
},
new Sprite
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
},
}
}
};
if (game.IsDeployedBuild)
checkForUpdateAsync();
}
protected override void LoadComplete()
{
base.LoadComplete();
var version = game.Version;
var lastVersion = config.Get<string>(OsuSetting.Version);
if (game.IsDeployedBuild && version != lastVersion)
{
config.Set(OsuSetting.Version, version);
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
notificationOverlay.Post(new UpdateCompleteNotification(version));
}
}
private class UpdateCompleteNotification : SimpleNotification
{
public UpdateCompleteNotification(string version)
{
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
return true;
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IconBackgound.Colour = colours.BlueDark;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
updateManager?.Dispose();
}
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
{
//should we schedule a retry on completion of this check?
bool scheduleRetry = true;
try
{
if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true);
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
if (info.ReleasesToApply.Count == 0)
//no updates available. bail and retry later.
return;
if (notification == null)
{
notification = new UpdateProgressNotification { State = ProgressNotificationState.Active };
Schedule(() => notificationOverlay.Post(notification));
}
Schedule(() =>
{
notification.Progress = 0;
notification.Text = @"Downloading update...";
});
try
{
await updateManager.DownloadReleases(info.ReleasesToApply, p => Schedule(() => notification.Progress = p / 100f));
Schedule(() =>
{
notification.Progress = 0;
notification.Text = @"Installing update...";
});
await updateManager.ApplyReleases(info, p => Schedule(() => notification.Progress = p / 100f));
Schedule(() => notification.State = ProgressNotificationState.Completed);
}
catch (Exception e)
{
if (useDeltaPatching)
{
Logger.Error(e, @"delta patching failed!");
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
//try again without deltas.
checkForUpdateAsync(false, notification);
scheduleRetry = false;
}
else
{
Logger.Error(e, @"update failed!");
}
}
}
catch (Exception)
{
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
}
finally
{
if (scheduleRetry)
{
//check again in 30 minutes.
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
if (notification != null)
notification.State = ProgressNotificationState.Cancelled;
}
}
}
protected override void PopIn()
{
this.FadeIn(1000);
}
protected override void PopOut()
{
}
private class UpdateProgressNotification : ProgressNotification
{
private OsuGame game;
protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification
{
Text = @"Update ready to install. Click to restart!",
Activated = () =>
{
// Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
UpdateManager.RestartAppWhenExited().Wait();
game.GracefullyExit();
return true;
}
};
[BackgroundDependencyLoader]
private void load(OsuColour colours, OsuGame game)
{
this.game = game;
IconContent.AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
},
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_upload,
Colour = Color4.White,
Size = new Vector2(20),
}
});
}
}
}
}
+63 -66
View File
@@ -1,66 +1,63 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
using System.Linq;
using osu.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
#if NET_FRAMEWORK
using System.Runtime;
#endif
namespace osu.Desktop
{
public static class Program
{
[STAThread]
public static int Main(string[] args)
{
// required to initialise native SQLite libraries on some platforms.
if (!RuntimeInfo.IsMono)
useMulticoreJit();
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
{
if (!host.IsPrimaryInstance)
{
var importer = new ArchiveImportIPCChannel(host);
// Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd);
foreach (var file in args)
{
Console.WriteLine(@"Importing {0}", file);
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000))
throw new TimeoutException(@"IPC took too long to send");
}
}
else
{
switch (args.FirstOrDefault() ?? string.Empty)
{
default:
host.Run(new OsuGameDesktop(args));
break;
}
}
return 0;
}
}
private static void useMulticoreJit()
{
#if NET_FRAMEWORK
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
ProfileOptimization.SetProfileRoot(directory.FullName);
ProfileOptimization.StartProfile("Startup.Profile");
#endif
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
using System.Linq;
using System.Runtime;
using osu.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
namespace osu.Desktop
{
public static class Program
{
[STAThread]
public static int Main(string[] args)
{
if (!RuntimeInfo.IsMono)
useMulticoreJit();
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
{
if (!host.IsPrimaryInstance)
{
var importer = new ArchiveImportIPCChannel(host);
// Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd);
foreach (var file in args)
{
Console.WriteLine(@"Importing {0}", file);
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000))
throw new TimeoutException(@"IPC took too long to send");
}
}
else
{
switch (args.FirstOrDefault() ?? string.Empty)
{
case "--tests":
host.Run(new OsuTestBrowser());
break;
default:
host.Run(new OsuGameDesktop(args));
break;
}
}
return 0;
}
}
private static void useMulticoreJit()
{
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
ProfileOptimization.SetProfileRoot(directory.FullName);
ProfileOptimization.StartProfile("Startup.Profile");
}
}
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("osu!lazer")]
[assembly: AssemblyDescription("click the circles. to the beat.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ppy Pty Ltd")]
[assembly: AssemblyProduct("osu!lazer")]
[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b0cb1d48-e4c2-4612-a347-beea7b1a71e7")]
[assembly: AssemblyVersion("0.0.0")]
[assembly: AssemblyFileVersion("0.0.0")]
@@ -1,46 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="osu!" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="osu!" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
If you want to utilize File and Registry Virtualization for backward
compatibility then delete the requestedExecutionLevel node.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>
+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Xml.XPath.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
+299 -38
View File
@@ -1,39 +1,300 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Game.props" />
<PropertyGroup Label="Project">
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
<OutputType>WinExe</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>click the circles. to the beat.</Description>
<AssemblyName>osu!</AssemblyName>
<Title>osu!lazer</Title>
<Product>osu!lazer</Product>
<ApplicationIcon>lazer.ico</ApplicationIcon>
<Version>0.0.0</Version>
<FileVersion>0.0.0</FileVersion>
</PropertyGroup>
<PropertyGroup Label="Defines">
<DefineConstants Condition="'$(TargetFramework)' == 'net471'">$(DefineConstants);NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<StartupObject>osu.Desktop.Program</StartupObject>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.3" />
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
</ItemGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<Import Project="..\osu.Game.props" />
<PropertyGroup>
<ProjectGuid>{419659FD-72EA-4678-9EB8-B22A746CED70}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop</RootNamespace>
<AssemblyName>osu!</AssemblyName>
<ManifestCertificateThumbprint>3CF060CD28877D0E3112948951A64B2A7CEEC909</ManifestCertificateThumbprint>
<ManifestKeyFile>codesigning.pfx</ManifestKeyFile>
<GenerateManifests>false</GenerateManifests>
<SignManifests>false</SignManifests>
<IsWebBootstrapper>false</IsWebBootstrapper>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<StartupObject>osu.Desktop.Program</StartupObject>
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>2</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<ProductVersion>12.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RunCodeAnalysis>false</RunCodeAnalysis>
<Prefer32Bit>false</Prefer32Bit>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<Commandlineparameters>
</Commandlineparameters>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>CuttingEdge NoUpdate</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Prefer32Bit>false</Prefer32Bit>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<Win32Resource>
</Win32Resource>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>lazer.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'VisualTests|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<WarningLevel>0</WarningLevel>
<NoStdLib>true</NoStdLib>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<StartArguments>--tests</StartArguments>
</PropertyGroup>
<ItemGroup>
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.MsDelta, Version=1.1.0.0, Culture=neutral, PublicKeyToken=46b2138a390abf55, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.MsDelta.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.PatchApi, Version=1.1.0.0, Culture=neutral, PublicKeyToken=3e8888ee913ed789, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
</Reference>
<Reference Include="Squirrel, Version=1.7.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
<HintPath>$(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="osu!.res" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
<Visible>False</Visible>
<ProductName>.NET Framework 2.0 %28x86%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
<Visible>False</Visible>
<ProductName>.NET Framework 3.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Compile Include="OsuGameDesktop.cs" />
<Compile Include="OsuTestBrowser.cs" />
<Compile Include="Overlays\VersionManager.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="lazer.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{d9a367c9-4c1a-489f-9b05-a0cea2b53b58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Shape\osu.Game.Rulesets.Shape.csproj">
<Project>{baf6d7b6-5e48-4278-ab81-f079af640e31}</Project>
<Name>osu.Game.Rulesets.Shape</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Vitaru\osu.Game.Rulesets.Vitaru.csproj">
<Project>{9a615027-b2ab-435e-b865-0c7209933457}</Project>
<Name>osu.Game.Rulesets.Vitaru</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj">
<Project>{54377672-20b1-40af-8087-5cf73bf3953a}</Project>
<Name>osu.Game.Tests</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\Symcol.Core\Symcol.Core.csproj">
<Project>{f34ac16c-e590-4d70-a069-a748326852bf}</Project>
<Name>Symcol.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Symcol.Rulesets.Core\Symcol.Rulesets.Core.csproj">
<Project>{552b5940-c647-4060-aa4d-61baac415c72}</Project>
<Name>Symcol.Rulesets.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<ProjectExtensions>
<VisualStudio>
</VisualStudio>
</ProjectExtensions>
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
</Target>
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
</Project>
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
<package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
<package id="squirrel.windows" version="1.7.8" targetFramework="net461" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
</packages>
-59
View File
@@ -1,59 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "VisualTests (Debug, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, dotnet)",
"env": {},
"console": "internalConsole"
}
]
}
-87
View File
@@ -1,87 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build (Debug, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:Configuration=Release",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Debug, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Restore (net471)",
"type": "shell",
"command": "nuget",
"args": [
"restore"
],
"problemMatcher": []
},
{
"label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
"restore"
],
"problemMatcher": []
}
]
}
@@ -1,62 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestCaseAutoJuiceStream : TestCasePlayer
{
public TestCaseAutoJuiceStream()
: base(new CatchRuleset())
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
Ruleset = ruleset.RulesetInfo
}
};
for (int i = 0; i < 100; i++)
{
float width = (i % 10 + 1) / 20f;
beatmap.HitObjects.Add(new JuiceStream
{
X = 0.5f - width / 2,
ControlPoints = new List<Vector2>
{
Vector2.Zero,
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
},
CurveType = CurveType.Linear,
Distance = width * CatchPlayfield.BASE_WIDTH,
StartTime = i * 2000,
NewCombo = i % 8 == 0
});
}
return beatmap;
}
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(beatmap, ruleset);
}
}
}
@@ -1,10 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
</ItemGroup>
</Project>
@@ -1,43 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmap : Beatmap<CatchHitObject>
{
public override IEnumerable<BeatmapStatistic> GetStatistics()
{
int fruits = HitObjects.Count(s => s is Fruit);
int juiceStreams = HitObjects.Count(s => s is JuiceStream);
int bananaShowers = HitObjects.Count(s => s is BananaShower);
return new[]
{
new BeatmapStatistic
{
Name = @"Fruit Count",
Content = fruits.ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Juice Stream Count",
Content = juiceStreams.ToString(),
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Banana Shower Count",
Content = bananaShowers.ToString(),
Icon = FontAwesome.fa_circle
}
};
}
}
}
@@ -1,75 +1,68 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{
public CatchBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
{
var curveData = obj as IHasCurve;
var positionData = obj as IHasXPosition;
var comboData = obj as IHasCombo;
var endTime = obj as IHasEndTime;
if (positionData == null)
{
if (endTime != null)
{
yield return new BananaShower
{
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false
};
}
yield break;
}
if (curveData != null)
{
yield return new JuiceStream
{
StartTime = obj.StartTime,
Samples = obj.Samples,
ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType,
Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples,
RepeatCount = curveData.RepeatCount,
X = positionData.X / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false
};
yield break;
}
yield return new Fruit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
NewCombo = comboData?.NewCombo ?? false,
X = positionData.X / CatchPlayfield.BASE_WIDTH
};
}
protected override Beatmap<CatchHitObject> CreateBeatmap() => new CatchBeatmap();
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
{
var curveData = obj as IHasCurve;
var positionData = obj as IHasXPosition;
var comboData = obj as IHasCombo;
var endTime = obj as IHasEndTime;
if (positionData == null)
yield break;
if (curveData != null)
{
yield return new JuiceStream
{
StartTime = obj.StartTime,
Samples = obj.Samples,
ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType,
Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples,
RepeatCount = curveData.RepeatCount,
X = positionData.X / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false
};
yield break;
}
if (endTime != null)
{
yield return new BananaShower
{
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false
};
yield break;
}
yield return new Fruit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
NewCombo = comboData?.NewCombo ?? false,
X = positionData.X / CatchPlayfield.BASE_WIDTH
};
}
}
}
@@ -1,77 +1,72 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmapProcessor : BeatmapProcessor
{
public CatchBeatmapProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
public override void PostProcess()
{
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
base.PostProcess();
int index = 0;
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
obj.IndexInBeatmap = index++;
}
private void initialiseHyperDash(List<CatchHitObject> objects)
{
// todo: add difficulty adjust.
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
int lastDirection = 0;
double lastExcess = halfCatcherWidth;
int objCount = objects.Count;
for (int i = 0; i < objCount - 1; i++)
{
CatchHitObject currentObject = objects[i];
// not needed?
// if (currentObject is TinyDroplet) continue;
CatchHitObject nextObject = objects[i + 1];
// while (nextObject is TinyDroplet)
// {
// if (++i == objCount - 1) break;
// nextObject = objects[i + 1];
// }
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
{
currentObject.HyperDashTarget = nextObject;
lastExcess = halfCatcherWidth;
}
else
{
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
}
lastDirection = thisDirection;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
{
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{
initialiseHyperDash(beatmap.HitObjects);
base.PostProcess(beatmap);
int index = 0;
foreach (var obj in beatmap.HitObjects)
obj.IndexInBeatmap = index++;
}
private void initialiseHyperDash(List<CatchHitObject> objects)
{
// todo: add difficulty adjust.
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
int lastDirection = 0;
double lastExcess = halfCatcherWidth;
int objCount = objects.Count;
for (int i = 0; i < objCount - 1; i++)
{
CatchHitObject currentObject = objects[i];
// not needed?
// if (currentObject is TinyDroplet) continue;
CatchHitObject nextObject = objects[i + 1];
// while (nextObject is TinyDroplet)
// {
// if (++i == objCount - 1) break;
// nextObject = objects[i + 1];
// }
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
{
currentObject.HyperDashTarget = nextObject;
lastExcess = halfCatcherWidth;
}
else
{
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
}
lastDirection = thisDirection;
}
}
}
}
@@ -1,18 +1,21 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyCalculator : DifficultyCalculator
{
public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Catch
{
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
{
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
}
}

Some files were not shown because too many files have changed in this diff Show More