Compare commits
143 Commits
physics
...
feat/liten
Author | SHA1 | Date | |
---|---|---|---|
64ee9a0789 | |||
452ce426a0 | |||
ad5f424298 | |||
9302c7e867 | |||
80725f454f | |||
fcc5350b56 | |||
1bf8238925 | |||
b641ebde08 | |||
9bf6cabe23 | |||
cd66eeeeef | |||
9f6958e5ba | |||
42394f4625 | |||
22f96458a6 | |||
a76905f31e | |||
e9ca71b87e | |||
46991699a1 | |||
08f52e9b72 | |||
9f8578af9b | |||
ac9cb72914 | |||
875f865bd3 | |||
15958ad402 | |||
1230dc84ab | |||
5d8f5d39be | |||
ceea23c5ae | |||
afd4ae8327 | |||
d41246b6f6 | |||
0ec047cc23 | |||
b238235c15 | |||
9c90a804c3 | |||
5cfed3ba56 | |||
bc6aaa865a | |||
5df09f64e2 | |||
abc467d1ea | |||
43c4eb6e4c | |||
0047111244 | |||
92357627a1 | |||
23fa1f06e9 | |||
d89af5ccad | |||
7cc3bb4d83 | |||
997ac72c4e | |||
0987bf7e91 | |||
648be2738f | |||
43875a6069 | |||
41f6dba5e4 | |||
2294c06bf9 | |||
b835fb1582 | |||
a0a3d97395 | |||
4710b8364c | |||
29df4a36e9 | |||
1ff26f5be4 | |||
e27b825990 | |||
4824496f1a | |||
f3b7ccaf70 | |||
475d82ba02 | |||
b3dd0f72a4 | |||
f3ff1b74d2 | |||
8901a5469f | |||
52682d42b6 | |||
0acd119099 | |||
3c38cb4159 | |||
59059fa09d | |||
cbda44c2d5 | |||
f20aabcacf | |||
da284eda95 | |||
d0ef2ef224 | |||
bddf00ce7a | |||
29829bbaa6 | |||
12f4950ffb | |||
0da5ac6f57 | |||
79922fadbe | |||
0f0180d435 | |||
ed6f783180 | |||
2853a6130b | |||
8f9c9f77f0 | |||
2b29636b4f | |||
4f3a2b4a4e | |||
57868ce178 | |||
214c37e63f | |||
54bdcd93b2 | |||
721d63cf66 | |||
03082ab43b | |||
6591326b70 | |||
0153d4cf69 | |||
94f7d4acfd | |||
23a0c8e893 | |||
cd3e23b427 | |||
d13233ce77 | |||
34aa4dbb69 | |||
51854a3e59 | |||
67e46cdaf3 | |||
73ae55e1d4 | |||
a35e25eb31 | |||
775f24c560 | |||
257e414c2a | |||
0cb8170530 | |||
bbf71f0be6 | |||
41a17219b6 | |||
29e79cdf26 | |||
b7a4fe9cb0 | |||
da83802427 | |||
364c383e08 | |||
1fd8060857 | |||
35933dfd14 | |||
d66b119119 | |||
e7ee460323 | |||
e86cf71010 | |||
a357f0fdcb | |||
ff3202eda9 | |||
8929d3282f | |||
bc7925dbab | |||
80ee5d760a | |||
de2d5fb446 | |||
753c0031cd | |||
a734f52b38 | |||
4cb9c7dc8d | |||
fe4618abd3 | |||
283556d329 | |||
5ef3d491c3 | |||
46f589d3aa | |||
b7d49e41c3 | |||
677fec7acc | |||
a9c7b8b660 | |||
958a75552f | |||
3717701171 | |||
4e95a3a420 | |||
ad60412d5f | |||
0b2158cc7b | |||
3580469a07 | |||
a7e8787579 | |||
45df32dabb | |||
b3af693678 | |||
c87827d1ef | |||
c2d2e1fd52 | |||
7ad2cc5912 | |||
ca92b12833 | |||
fbe3087502 | |||
ef347320fd | |||
1207d04d50 | |||
26f11b5cc0 | |||
481a49bb52 | |||
ffe4fc044e | |||
91b05c97c4 | |||
11074b89d5 |
484
.gitignore
vendored
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from `dotnet new gitignore`
|
||||||
|
|
||||||
|
# dotenv files
|
||||||
|
.env
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# Tye
|
||||||
|
.tye/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.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
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# Note: 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
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable 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
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# 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
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# 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
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# 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/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
##
|
||||||
|
## Visual studio for Mac
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
# globs
|
||||||
|
Makefile.in
|
||||||
|
*.userprefs
|
||||||
|
*.usertasks
|
||||||
|
config.make
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# Mac bundle stuff
|
||||||
|
*.dmg
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# Vim temporary swap files
|
||||||
|
*.swp
|
80
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": ".NET Launch (Client) 1",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-client",
|
||||||
|
"program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net9.0/Desktop.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"console": "internalConsole",
|
||||||
|
"justMyCode": false,
|
||||||
|
"logging": {
|
||||||
|
"moduleLoad": false,
|
||||||
|
"programOutput": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Launch (Client) 2",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-client",
|
||||||
|
"program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net9.0/Desktop.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"console": "internalConsole",
|
||||||
|
"justMyCode": false,
|
||||||
|
"logging": {
|
||||||
|
"moduleLoad": false,
|
||||||
|
"programOutput": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Launch (Server)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-server",
|
||||||
|
"program": "${workspaceFolder}/Platforms/Server/bin/Debug/net9.0/Server.dll",
|
||||||
|
"args": [],
|
||||||
|
"env": {
|
||||||
|
"PORT": "8888",
|
||||||
|
},
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"console": "internalConsole",
|
||||||
|
"justMyCode": false,
|
||||||
|
"logging": {
|
||||||
|
"moduleLoad": false,
|
||||||
|
"programOutput": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": ".NET Launch Client & Server",
|
||||||
|
"configurations": [
|
||||||
|
".NET Launch (Server)",
|
||||||
|
".NET Launch (Client) 1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Launch 2 Client & 1 Server",
|
||||||
|
"configurations": [
|
||||||
|
".NET Launch (Server)",
|
||||||
|
".NET Launch (Client) 1",
|
||||||
|
".NET Launch (Client) 2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Launch 2 Clients",
|
||||||
|
"configurations": [
|
||||||
|
".NET Launch (Client) 1",
|
||||||
|
".NET Launch (Client) 2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
33
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build-client",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/Platforms/Desktop"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-server",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/Platforms/Server"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
|
||||||
|
WORKDIR /App
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
|
RUN dotnet restore
|
||||||
|
RUN dotnet publish Platforms/Server -c Release -r linux-musl-x64 --self-contained true -o out
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /App
|
||||||
|
COPY --from=build /App/out .
|
||||||
|
|
||||||
|
RUN apk add --no-cache icu-libs
|
||||||
|
|
||||||
|
ENTRYPOINT ["./Server"]
|
2
Engine
27
Game/.vscode/launch.json
vendored
@@ -1,27 +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": ".NET Core Launch (console)",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "build",
|
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
|
||||||
"program": "${workspaceFolder}/bin/Debug/net8.0/${workspaceFolderBasename}.dll",
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
|
||||||
"console": "internalConsole",
|
|
||||||
"stopAtEntry": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": ".NET Core Attach",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "attach",
|
|
||||||
"processId": "${command:pickProcess}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
7
Game/.vscode/settings.json
vendored
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"cSpell.words": [
|
|
||||||
"AABB",
|
|
||||||
"DAABB",
|
|
||||||
"Syntriax"
|
|
||||||
]
|
|
||||||
}
|
|
15
Game/.vscode/tasks.json
vendored
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"type": "dotnet",
|
|
||||||
"task": "build",
|
|
||||||
"problemMatcher": ["$msCompile"],
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
},
|
|
||||||
"label": "build"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
using Microsoft.Xna.Framework;
|
|
||||||
|
|
||||||
using Apos.Shapes;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Physics2D.Primitives;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class CircleBehaviour : Syntriax.Engine.Physics2D.Collider2DCircleBehaviour, IDisplayableShape
|
|
||||||
{
|
|
||||||
public CircleBehaviour(Circle circle) { this.CircleLocal = circle; }
|
|
||||||
public CircleBehaviour(Circle circle, float Thickness) { this.CircleLocal = circle; this.Thickness = Thickness; }
|
|
||||||
public CircleBehaviour(Circle circle, Color color) { this.CircleLocal = circle; Color = color; }
|
|
||||||
public CircleBehaviour(Circle circle, Color color, float Thickness) { this.CircleLocal = circle; this.Thickness = Thickness; Color = color; }
|
|
||||||
|
|
||||||
public Color Color { get; set; } = Color.White;
|
|
||||||
public float Thickness { get; set; } = .5f;
|
|
||||||
|
|
||||||
public void Draw(ShapeBatch shapeBatch)
|
|
||||||
{
|
|
||||||
shapeBatch.BorderCircle(CircleWorld.Center.ToDisplayVector2(), CircleWorld.Radius, Color);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
using Apos.Shapes;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public interface IDisplayableShape
|
|
||||||
{
|
|
||||||
void Draw(ShapeBatch shapeBatch);
|
|
||||||
}
|
|
@@ -1,107 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Input;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
|
|
||||||
{
|
|
||||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnPressed = new(256);
|
|
||||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnReleased = new(256);
|
|
||||||
|
|
||||||
private int cachePressedCurrentlyCount = 0;
|
|
||||||
private readonly Keys[] cachePressedCurrently = new Keys[256];
|
|
||||||
|
|
||||||
private int cachePressedPreviouslyCount = 0;
|
|
||||||
private readonly Keys[] cachePressedPreviously = new Keys[256];
|
|
||||||
|
|
||||||
public void RegisterOnPress(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
|
||||||
{
|
|
||||||
if (OnPressed.TryGetValue(key, out var action))
|
|
||||||
{
|
|
||||||
action += callback;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPressed.Add(key, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnregisterOnPress(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
|
||||||
{
|
|
||||||
if (OnPressed.TryGetValue(key, out var action))
|
|
||||||
action -= callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterOnRelease(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
|
||||||
{
|
|
||||||
if (OnReleased.TryGetValue(key, out var action))
|
|
||||||
{
|
|
||||||
action += callback;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnReleased.Add(key, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnregisterOnRelease(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
|
||||||
{
|
|
||||||
if (OnReleased.TryGetValue(key, out var action))
|
|
||||||
action -= callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnUpdate()
|
|
||||||
{
|
|
||||||
KeyboardState keyboardState = Keyboard.GetState();
|
|
||||||
keyboardState.GetPressedKeys(cachePressedCurrently);
|
|
||||||
cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount();
|
|
||||||
|
|
||||||
for (int i = 0; i < cachePressedCurrentlyCount; i++)
|
|
||||||
{
|
|
||||||
Keys currentlyPressedKey = cachePressedCurrently[i];
|
|
||||||
|
|
||||||
if (!OnPressed.TryGetValue(currentlyPressedKey, out var action))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (WasPressed(currentlyPressedKey))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
action.Invoke(this, currentlyPressedKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
|
||||||
{
|
|
||||||
Keys previouslyPressedKey = cachePressedPreviously[i];
|
|
||||||
|
|
||||||
if (!OnReleased.TryGetValue(previouslyPressedKey, out var action))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (IsPressed(previouslyPressedKey))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
action.Invoke(this, previouslyPressedKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
|
|
||||||
cachePressedPreviouslyCount = cachePressedCurrentlyCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPressed(Keys key)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < cachePressedCurrentlyCount; i++)
|
|
||||||
if (cachePressedCurrently[i] == key)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool WasPressed(Keys key)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
|
||||||
if (cachePressedPreviously[i] == key)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,106 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class MonoGameCameraBehaviour : BehaviourOverride, ICamera
|
|
||||||
{
|
|
||||||
public Action<ICamera>? OnPositionChanged { get; set; } = null;
|
|
||||||
public Action<ICamera>? OnMatrixTransformChanged { get; set; } = null;
|
|
||||||
public Action<ICamera>? OnViewportChanged { get; set; } = null;
|
|
||||||
public Action<ICamera>? OnRotationChanged { get; set; } = null;
|
|
||||||
public Action<ICamera>? OnZoomChanged { get; set; } = null;
|
|
||||||
|
|
||||||
private Matrix _matrixTransform = Matrix.Identity;
|
|
||||||
|
|
||||||
private Viewport _viewport = default;
|
|
||||||
|
|
||||||
private float _zoom = 1f;
|
|
||||||
|
|
||||||
public Matrix MatrixTransform
|
|
||||||
{
|
|
||||||
get => _matrixTransform;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_matrixTransform == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_matrixTransform = value;
|
|
||||||
OnMatrixTransformChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2D Position
|
|
||||||
{
|
|
||||||
get => Transform.Position;
|
|
||||||
set => Transform.Position = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Viewport Viewport
|
|
||||||
{
|
|
||||||
get => _viewport;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_viewport.Equals(value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_viewport = value;
|
|
||||||
OnViewportChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Zoom
|
|
||||||
{
|
|
||||||
get => _zoom;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
float newValue = value >= .1f ? value : .1f;
|
|
||||||
|
|
||||||
if (_zoom == newValue)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_zoom = newValue;
|
|
||||||
OnZoomChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Rotation
|
|
||||||
{
|
|
||||||
get => Transform.Rotation;
|
|
||||||
set => Transform.Rotation = value;
|
|
||||||
}
|
|
||||||
public Action<IAssignableTransform>? OnTransformAssigned { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
|
||||||
|
|
||||||
ITransform IAssignableTransform.Transform => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
MatrixTransform =
|
|
||||||
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
|
|
||||||
Matrix.CreateRotationZ(Rotation) *
|
|
||||||
Matrix.CreateScale(Zoom) *
|
|
||||||
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
Transform.OnRotationChanged += OnTransformRotationChanged;
|
|
||||||
Transform.OnPositionChanged += OnTransformPositionChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFinalize()
|
|
||||||
{
|
|
||||||
Transform.OnRotationChanged -= OnTransformRotationChanged;
|
|
||||||
Transform.OnPositionChanged -= OnTransformPositionChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTransformRotationChanged(ITransform _) => OnRotationChanged?.Invoke(this);
|
|
||||||
private void OnTransformPositionChanged(ITransform _) => OnPositionChanged?.Invoke(this);
|
|
||||||
|
|
||||||
public bool Assign(ITransform transform) => GameObject.Assign(transform);
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Input;
|
|
||||||
using Syntriax.Engine.Physics2D.Abstract;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
public class MovementBallBehaviour(Vector2D StartDirection, float Speed) : BehaviourOverride
|
|
||||||
{
|
|
||||||
public Vector2D StartDirection { get; private set; } = Vector2D.Normalize(StartDirection);
|
|
||||||
public float Speed { get; set; } = Speed;
|
|
||||||
|
|
||||||
protected override void OnFirstActiveFrame()
|
|
||||||
{
|
|
||||||
if (!BehaviourController.TryGetBehaviour(out IRigidBody2D? rigidBody))
|
|
||||||
throw new Exception($"Where's my {nameof(IRigidBody2D)}????");
|
|
||||||
|
|
||||||
rigidBody.Velocity = StartDirection * Speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// protected override void OnUpdate(GameTime time)
|
|
||||||
// {
|
|
||||||
// GameObject.Transform.Position += StartDirection * (time.ElapsedGameTime.Nanoseconds * .001f) * Speed;
|
|
||||||
|
|
||||||
// float absY = MathF.Abs(GameObject.Transform.Position.Y);
|
|
||||||
// float differenceY = absY - PlayAreaBehaviour.PlayArea.Y * 0.5f;
|
|
||||||
// if (differenceY > 0f)
|
|
||||||
// {
|
|
||||||
// if (GameObject.Transform.Position.Y > 0f)
|
|
||||||
// GameObject.Transform.Position -= Vector2.UnitY * differenceY * 2f;
|
|
||||||
// else
|
|
||||||
// GameObject.Transform.Position += Vector2.UnitY * differenceY * 2f;
|
|
||||||
|
|
||||||
// StartDirection = new(StartDirection.X, -StartDirection.Y);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// float absX = MathF.Abs(GameObject.Transform.Position.X);
|
|
||||||
// float differenceX = absX - PlayAreaBehaviour.PlayArea.X * 0.5f;
|
|
||||||
// if (differenceX > 0f)
|
|
||||||
// {
|
|
||||||
// if (GameObject.Transform.Position.X > 0f)
|
|
||||||
// GameObject.Transform.Position -= Vector2.UnitX * differenceX * 2f;
|
|
||||||
// else
|
|
||||||
// GameObject.Transform.Position += Vector2.UnitX * differenceX * 2f;
|
|
||||||
|
|
||||||
// StartDirection = new(-StartDirection.X, StartDirection.Y);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Input;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class MovementBoxBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : BehaviourOverride
|
|
||||||
{
|
|
||||||
private Keys Up { get; } = Up;
|
|
||||||
private Keys Down { get; } = Down;
|
|
||||||
public float High { get; } = High;
|
|
||||||
public float Low { get; } = Low;
|
|
||||||
public float Speed { get; set; } = Speed;
|
|
||||||
|
|
||||||
private bool isUpPressed = false;
|
|
||||||
private bool isDownPressed = false;
|
|
||||||
|
|
||||||
private IButtonInputs<Keys> inputs = null!;
|
|
||||||
|
|
||||||
protected override void OnUpdate()
|
|
||||||
{
|
|
||||||
if (isUpPressed && isDownPressed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (isUpPressed)
|
|
||||||
GameObject.Transform.Position = GameObject.Transform.Position + Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed;
|
|
||||||
else if (isDownPressed)
|
|
||||||
GameObject.Transform.Position = GameObject.Transform.Position + -Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed;
|
|
||||||
|
|
||||||
GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFirstActiveFrame()
|
|
||||||
{
|
|
||||||
if (!BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult))
|
|
||||||
throw new Exception($"{nameof(IButtonInputs<Keys>)} is missing on ${GameObject.Name}.");
|
|
||||||
|
|
||||||
inputs = behaviourResult;
|
|
||||||
|
|
||||||
inputs.RegisterOnPress(Up, OnUpPressed);
|
|
||||||
inputs.RegisterOnRelease(Up, OnUpReleased);
|
|
||||||
|
|
||||||
inputs.RegisterOnPress(Down, OnDownPressed);
|
|
||||||
inputs.RegisterOnRelease(Down, OnDownReleased);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFinalize()
|
|
||||||
{
|
|
||||||
inputs.UnregisterOnPress(Up, OnUpPressed);
|
|
||||||
inputs.UnregisterOnRelease(Up, OnUpReleased);
|
|
||||||
|
|
||||||
inputs.UnregisterOnPress(Down, OnDownPressed);
|
|
||||||
inputs.UnregisterOnRelease(Down, OnDownReleased);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) => isUpPressed = true;
|
|
||||||
private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) => isUpPressed = false;
|
|
||||||
private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = true;
|
|
||||||
private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = false;
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class MovementMouseBehaviour : BehaviourOverride
|
|
||||||
{
|
|
||||||
protected override void OnUpdate()
|
|
||||||
{
|
|
||||||
GameObject.Transform.Position = Mouse.GetState().Position.ToVector2D().ApplyDisplayScale();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class PlayAreaBehaviour : BehaviourOverride
|
|
||||||
{
|
|
||||||
public Action<PlayAreaBehaviour>? OnPlayAreaChanged { get; set; } = null;
|
|
||||||
|
|
||||||
private Vector2 _playArea = Vector2.Zero;
|
|
||||||
|
|
||||||
public Vector2 PlayArea
|
|
||||||
{
|
|
||||||
get => _playArea;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_playArea == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_playArea = value;
|
|
||||||
OnPlayAreaChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,25 +0,0 @@
|
|||||||
using Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class RotatableBehaviour : BehaviourOverride
|
|
||||||
{
|
|
||||||
private KeyboardInputsBehaviour? inputs = null;
|
|
||||||
|
|
||||||
protected override void OnFirstActiveFrame()
|
|
||||||
{
|
|
||||||
if (!BehaviourController.TryGetBehaviour(out inputs))
|
|
||||||
inputs = BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnUpdate()
|
|
||||||
{
|
|
||||||
if (inputs is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (inputs.IsPressed(Microsoft.Xna.Framework.Input.Keys.NumPad4))
|
|
||||||
Transform.Rotation += Time.Elapsed.Nanoseconds * 0.0025f;
|
|
||||||
if (inputs.IsPressed(Microsoft.Xna.Framework.Input.Keys.NumPad6))
|
|
||||||
Transform.Rotation -= Time.Elapsed.Nanoseconds * 0.0025f;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,47 +0,0 @@
|
|||||||
using Microsoft.Xna.Framework;
|
|
||||||
|
|
||||||
using Apos.Shapes;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Input;
|
|
||||||
using Syntriax.Engine.Physics2D.Abstract;
|
|
||||||
using Syntriax.Engine.Physics2D.Primitives;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class ShapeAABBBehaviour : BehaviourOverride, IDisplayableShape
|
|
||||||
{
|
|
||||||
private IShapeCollider2D? shapeCollider = null;
|
|
||||||
|
|
||||||
public ShapeAABBBehaviour() { }
|
|
||||||
public ShapeAABBBehaviour(float Thickness) { this.Thickness = Thickness; }
|
|
||||||
public ShapeAABBBehaviour(Color color) { Color = color; }
|
|
||||||
public ShapeAABBBehaviour(Color color, float Thickness) { this.Thickness = Thickness; Color = color; }
|
|
||||||
|
|
||||||
public Color Color { get; set; } = Color.White;
|
|
||||||
public float Thickness { get; set; } = .5f;
|
|
||||||
public bool display = true;
|
|
||||||
|
|
||||||
protected override void OnFirstActiveFrame()
|
|
||||||
{
|
|
||||||
BehaviourController.TryGetBehaviour(out shapeCollider);
|
|
||||||
|
|
||||||
if (BehaviourController.TryGetBehaviour(out IButtonInputs<Microsoft.Xna.Framework.Input.Keys>? keys))
|
|
||||||
keys.RegisterOnPress(Microsoft.Xna.Framework.Input.Keys.D, (_1, _2) => display = !display);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(ShapeBatch shapeBatch)
|
|
||||||
{
|
|
||||||
if (!display)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (shapeCollider is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AABB aabb = AABB.FromVectors(shapeCollider.ShapeWorld);
|
|
||||||
|
|
||||||
shapeBatch.BorderCircle(aabb.Center.ToDisplayVector2(), 7.5f, Color.Beige);
|
|
||||||
|
|
||||||
shapeBatch.DrawRectangle(aabb.Center.ApplyDisplayScale().Subtract(aabb.SizeHalf).ToVector2(), aabb.Size.ToVector2(), Color.Transparent, Color.Blue);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
using Microsoft.Xna.Framework;
|
|
||||||
|
|
||||||
using Apos.Shapes;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Physics2D.Primitives;
|
|
||||||
|
|
||||||
namespace Pong.Behaviours;
|
|
||||||
|
|
||||||
public class ShapeBehaviour : Syntriax.Engine.Physics2D.Collider2DShapeBehaviour, IDisplayableShape
|
|
||||||
{
|
|
||||||
public ShapeBehaviour(Shape Shape) { this.ShapeLocal = Shape; }
|
|
||||||
public ShapeBehaviour(Shape Shape, float Thickness) { this.ShapeLocal = Shape; this.Thickness = Thickness; }
|
|
||||||
public ShapeBehaviour(Shape Shape, Color color) { this.ShapeLocal = Shape; Color = color; }
|
|
||||||
public ShapeBehaviour(Shape Shape, Color color, float Thickness) { this.ShapeLocal = Shape; this.Thickness = Thickness; Color = color; }
|
|
||||||
|
|
||||||
public Color Color { get; set; } = Color.White;
|
|
||||||
public float Thickness { get; set; } = .5f;
|
|
||||||
|
|
||||||
public void Draw(ShapeBatch shapeBatch)
|
|
||||||
{
|
|
||||||
int count = ShapeWorld.Vertices.Count;
|
|
||||||
|
|
||||||
shapeBatch.BorderCircle(Transform.Position.ToDisplayVector2(), 5f, Color.DarkRed);
|
|
||||||
|
|
||||||
for (int i = 0; i < count - 1; i++)
|
|
||||||
shapeBatch.DrawLine(ShapeWorld[i].ToDisplayVector2(), ShapeWorld[i + 1].ToDisplayVector2(), Thickness, Color, Color);
|
|
||||||
shapeBatch.DrawLine(ShapeWorld[0].ToDisplayVector2(), ShapeWorld[^1].ToDisplayVector2(), Thickness, Color, Color);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,63 +0,0 @@
|
|||||||
|
|
||||||
#----------------------------- Global Properties ----------------------------#
|
|
||||||
|
|
||||||
/outputDir:bin/$(Platform)
|
|
||||||
/intermediateDir:obj/$(Platform)
|
|
||||||
/platform:DesktopGL
|
|
||||||
/config:
|
|
||||||
/profile:Reach
|
|
||||||
/compress:False
|
|
||||||
|
|
||||||
#-------------------------------- References --------------------------------#
|
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------- Content ---------------------------------#
|
|
||||||
|
|
||||||
#begin Sprites/Circle.png
|
|
||||||
/importer:TextureImporter
|
|
||||||
/processor:TextureProcessor
|
|
||||||
/processorParam:ColorKeyColor=255,0,255,255
|
|
||||||
/processorParam:ColorKeyEnabled=True
|
|
||||||
/processorParam:GenerateMipmaps=False
|
|
||||||
/processorParam:PremultiplyAlpha=True
|
|
||||||
/processorParam:ResizeToPowerOfTwo=False
|
|
||||||
/processorParam:MakeSquare=False
|
|
||||||
/processorParam:TextureFormat=Color
|
|
||||||
/build:Sprites/Circle.png
|
|
||||||
|
|
||||||
#begin Sprites/Circle.png
|
|
||||||
/importer:TextureImporter
|
|
||||||
/processor:TextureProcessor
|
|
||||||
/processorParam:ColorKeyColor=255,0,255,255
|
|
||||||
/processorParam:ColorKeyEnabled=True
|
|
||||||
/processorParam:GenerateMipmaps=False
|
|
||||||
/processorParam:PremultiplyAlpha=True
|
|
||||||
/processorParam:ResizeToPowerOfTwo=False
|
|
||||||
/processorParam:MakeSquare=False
|
|
||||||
/processorParam:TextureFormat=Color
|
|
||||||
/build:Sprites/Circle.png
|
|
||||||
|
|
||||||
#begin Sprites/Pixel.png
|
|
||||||
/importer:TextureImporter
|
|
||||||
/processor:TextureProcessor
|
|
||||||
/processorParam:ColorKeyColor=255,0,255,255
|
|
||||||
/processorParam:ColorKeyEnabled=True
|
|
||||||
/processorParam:GenerateMipmaps=False
|
|
||||||
/processorParam:PremultiplyAlpha=True
|
|
||||||
/processorParam:ResizeToPowerOfTwo=False
|
|
||||||
/processorParam:MakeSquare=False
|
|
||||||
/processorParam:TextureFormat=Color
|
|
||||||
/build:Sprites/Pixel.png
|
|
||||||
|
|
||||||
#begin Sprites/Pixel.png
|
|
||||||
/importer:TextureImporter
|
|
||||||
/processor:TextureProcessor
|
|
||||||
/processorParam:ColorKeyColor=255,0,255,255
|
|
||||||
/processorParam:ColorKeyEnabled=True
|
|
||||||
/processorParam:GenerateMipmaps=False
|
|
||||||
/processorParam:PremultiplyAlpha=True
|
|
||||||
/processorParam:ResizeToPowerOfTwo=False
|
|
||||||
/processorParam:MakeSquare=False
|
|
||||||
/processorParam:TextureFormat=Color
|
|
||||||
/build:Sprites/Pixel.png
|
|
||||||
|
|
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 546 B |
@@ -1,23 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
namespace Pong;
|
|
||||||
|
|
||||||
public static class EngineConverter
|
|
||||||
{
|
|
||||||
public readonly static Vector2D screenScale = Vector2D.Down + Vector2D.Right;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static EngineTime ToEngineTime(this GameTime gameTime) => new(gameTime.TotalGameTime, gameTime.ElapsedGameTime);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Vector2 ToVector2(this Vector2D vector) => new(vector.X, vector.Y);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Vector2 ToDisplayVector2(this Vector2D vector) => vector.Scale(screenScale).ToVector2();
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Vector2D ApplyDisplayScale(this Vector2D vector) => vector.Scale(screenScale);
|
|
||||||
}
|
|
@@ -1,36 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>WinExe</OutputType>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<RollForward>Major</RollForward>
|
|
||||||
<PublishReadyToRun>false</PublishReadyToRun>
|
|
||||||
<TieredCompilation>false</TieredCompilation>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
|
||||||
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="Icon.ico" />
|
|
||||||
<None Remove="Icon.bmp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="Icon.ico" />
|
|
||||||
<EmbeddedResource Include="Icon.bmp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Apos.Shapes" Version="0.2.3" />
|
|
||||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
|
||||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Engine\Engine.Core\Engine.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\Engine\Engine.Input\Engine.Input.csproj" />
|
|
||||||
<ProjectReference Include="..\Engine\Engine.Physics2D\Engine.Physics2D.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
|
|
||||||
<Message Text="Restoring dotnet tools" Importance="High" />
|
|
||||||
<Exec Command="dotnet tool restore" />
|
|
||||||
</Target>
|
|
||||||
</Project>
|
|
245
Game/Game1.cs
@@ -1,245 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
|
|
||||||
using Pong.Behaviours;
|
|
||||||
using Apos.Shapes;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Physics2D;
|
|
||||||
using Syntriax.Engine.Physics2D.Primitives;
|
|
||||||
|
|
||||||
namespace Pong;
|
|
||||||
|
|
||||||
public class Game1 : Game
|
|
||||||
{
|
|
||||||
private GraphicsDeviceManager _graphics = null!;
|
|
||||||
private PhysicsEngine2D engine;
|
|
||||||
private SpriteBatch _spriteBatch = null!;
|
|
||||||
private ShapeBatch _shapeBatch = null!;
|
|
||||||
public static GameManager gameManager = null!;
|
|
||||||
public static Sprite spriteBox = null!;
|
|
||||||
private MonoGameCameraBehaviour cameraBehaviour = null!;
|
|
||||||
|
|
||||||
|
|
||||||
public Game1()
|
|
||||||
{
|
|
||||||
engine = new PhysicsEngine2D();
|
|
||||||
|
|
||||||
_graphics = new GraphicsDeviceManager(this)
|
|
||||||
{
|
|
||||||
PreferredBackBufferWidth = 1024,
|
|
||||||
PreferredBackBufferHeight = 576,
|
|
||||||
GraphicsProfile = GraphicsProfile.HiDef
|
|
||||||
};
|
|
||||||
|
|
||||||
Content.RootDirectory = "Content";
|
|
||||||
IsMouseVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
gameManager = new();
|
|
||||||
|
|
||||||
// TODO: Add your initialization logic here
|
|
||||||
gameManager.Initialize();
|
|
||||||
|
|
||||||
base.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadContent()
|
|
||||||
{
|
|
||||||
_spriteBatch = new SpriteBatch(GraphicsDevice);
|
|
||||||
_shapeBatch = new ShapeBatch(GraphicsDevice, Content);
|
|
||||||
|
|
||||||
// spriteBox = new Sprite() { Texture2D = Content.Load<Texture2D>("Sprites/Pixel") };
|
|
||||||
// Sprite spriteBall = new Sprite() { Texture2D = Content.Load<Texture2D>("Sprites/Circle") };
|
|
||||||
|
|
||||||
IGameObject gameObjectCamera = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
gameObjectCamera.Name = "Camera";
|
|
||||||
gameObjectCamera.Transform.Position = Vector2D.Zero;
|
|
||||||
|
|
||||||
cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour<MonoGameCameraBehaviour>();
|
|
||||||
cameraBehaviour.Viewport = GraphicsDevice.Viewport;
|
|
||||||
gameManager.Camera = cameraBehaviour;
|
|
||||||
|
|
||||||
|
|
||||||
// IGameObject gameObjectCircle = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
// gameObjectCircle.Name = "Circle";
|
|
||||||
// gameObjectCircle.Transform.Position = new Vector2D(0f, -50f);
|
|
||||||
// gameObjectCircle.Transform.Scale = new Vector2D(25f, 25f);
|
|
||||||
// gameObjectCircle.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
|
||||||
// gameObjectCircle.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
|
|
||||||
// gameObjectCircle.BehaviourController.AddBehaviour<CircleBehaviour>(new Circle(Vector2D.Zero, 1f));
|
|
||||||
// engine.AddRigidBody(gameObjectCircle.BehaviourController.AddBehaviour<RigidBody2D>());
|
|
||||||
|
|
||||||
IGameObject gameObjectCircle2 = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
gameObjectCircle2.Name = "Circle2";
|
|
||||||
gameObjectCircle2.Transform.Position = new Vector2D(5f, 50f);
|
|
||||||
gameObjectCircle2.Transform.Scale = new Vector2D(25f, 25f);
|
|
||||||
gameObjectCircle2.BehaviourController.AddBehaviour<MovementMouseBehaviour>();
|
|
||||||
gameObjectCircle2.BehaviourController.AddBehaviour<CircleBehaviour>(new Circle(Vector2D.Zero, 1f));
|
|
||||||
engine.AddRigidBody(gameObjectCircle2.BehaviourController.AddBehaviour<RigidBody2D>());
|
|
||||||
|
|
||||||
IGameObject gameObjectDiamond = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
gameObjectDiamond.Name = "Diamond";
|
|
||||||
gameObjectDiamond.Transform.Position = new Vector2D(-150f, -150f);
|
|
||||||
gameObjectDiamond.Transform.Scale = new Vector2D(50f, 50f);
|
|
||||||
gameObjectDiamond.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
|
||||||
gameObjectDiamond.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
|
|
||||||
gameObjectDiamond.BehaviourController.AddBehaviour<RotatableBehaviour>();
|
|
||||||
gameObjectDiamond.BehaviourController.AddBehaviour<ShapeBehaviour>(new Shape([Vector2D.Up, Vector2D.One, Vector2D.Right, Vector2D.Down, -Vector2D.One, Vector2D.Left]));
|
|
||||||
gameObjectDiamond.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
|
|
||||||
engine.AddRigidBody(gameObjectDiamond.BehaviourController.AddBehaviour<RigidBody2D>());
|
|
||||||
|
|
||||||
IGameObject gameObjectBox = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
gameObjectBox.Name = "Box";
|
|
||||||
gameObjectBox.Transform.Position = new Vector2D(150f, -150f);
|
|
||||||
gameObjectBox.Transform.Scale = new Vector2D(100f, 100f);
|
|
||||||
gameObjectBox.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
|
||||||
gameObjectBox.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
|
|
||||||
gameObjectBox.BehaviourController.AddBehaviour<RotatableBehaviour>();
|
|
||||||
gameObjectBox.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Pentagon);
|
|
||||||
gameObjectBox.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
|
|
||||||
engine.AddRigidBody(gameObjectBox.BehaviourController.AddBehaviour<RigidBody2D>());
|
|
||||||
|
|
||||||
for (int i = 3; i < 10; i++)
|
|
||||||
{
|
|
||||||
IGameObject Test = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
Test.Name = i.ToString();
|
|
||||||
Test.Transform.Position = new Vector2D((i - 6) * 150, 0f);
|
|
||||||
Test.Transform.Scale = new Vector2D(75f, 75f);
|
|
||||||
Test.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
|
||||||
Test.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
|
|
||||||
Test.BehaviourController.AddBehaviour<RotatableBehaviour>();
|
|
||||||
Test.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.CreateNgon(i));
|
|
||||||
Test.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
|
|
||||||
RigidBody2D rigidBody = Test.BehaviourController.AddBehaviour<RigidBody2D>();
|
|
||||||
rigidBody.AngularVelocity = 90f;
|
|
||||||
engine.AddRigidBody(rigidBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// IGameObject gameObjectShape = gameManager.InstantiateGameObject<GameObject>();
|
|
||||||
// gameObjectShape.Name = "Shape";
|
|
||||||
// gameObjectShape.Transform.Position = new Vector2D(250f, 0f);
|
|
||||||
// gameObjectShape.Transform.Scale = new Vector2D(100f, 100f);
|
|
||||||
// gameObjectShape.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
|
||||||
// gameObjectShape.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
|
|
||||||
// gameObjectShape.BehaviourController.AddBehaviour<RotatableBehaviour>();
|
|
||||||
// gameObjectShape.BehaviourController.AddBehaviour<ShapeBehaviour>(new Shape([Vector2D.Up, Vector2D.One, Vector2D.Right, Vector2D.Down, Vector2D.Zero, Vector2D.Left]));
|
|
||||||
// gameObjectShape.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update(GameTime gameTime)
|
|
||||||
{
|
|
||||||
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
|
|
||||||
Exit();
|
|
||||||
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.F))
|
|
||||||
{
|
|
||||||
if (_graphics.IsFullScreen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_graphics.PreferMultiSampling = false;
|
|
||||||
_graphics.PreferredBackBufferWidth = GraphicsDevice.Adapter.CurrentDisplayMode.Width;
|
|
||||||
_graphics.PreferredBackBufferHeight = GraphicsDevice.Adapter.CurrentDisplayMode.Height;
|
|
||||||
_graphics.IsFullScreen = true;
|
|
||||||
_graphics.ApplyChanges();
|
|
||||||
|
|
||||||
float previousScreenSize = MathF.Sqrt(MathF.Pow(cameraBehaviour.Viewport.Width, 2f) + MathF.Pow(cameraBehaviour.Viewport.Height, 2f));
|
|
||||||
float currentScreenSize = MathF.Sqrt(MathF.Pow(GraphicsDevice.Viewport.Width, 2f) + MathF.Pow(GraphicsDevice.Viewport.Height, 2f));
|
|
||||||
cameraBehaviour.Zoom /= previousScreenSize / currentScreenSize;
|
|
||||||
cameraBehaviour.Viewport = GraphicsDevice.Viewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.U))
|
|
||||||
cameraBehaviour.Zoom += gameTime.ElapsedGameTime.Nanoseconds * 0.00025f;
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.J))
|
|
||||||
cameraBehaviour.Zoom -= gameTime.ElapsedGameTime.Nanoseconds * 0.00025f;
|
|
||||||
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.Q))
|
|
||||||
cameraBehaviour.BehaviourController.GameObject.Transform.Rotation += gameTime.ElapsedGameTime.Nanoseconds * 0.000025f;
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.E))
|
|
||||||
cameraBehaviour.BehaviourController.GameObject.Transform.Rotation -= gameTime.ElapsedGameTime.Nanoseconds * 0.000025f;
|
|
||||||
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.N))
|
|
||||||
{
|
|
||||||
seconds = 70f;
|
|
||||||
while (physicsTimer + 0.01f < seconds)
|
|
||||||
{
|
|
||||||
physicsTimer += 0.01f;
|
|
||||||
engine.Step(.01f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.M))
|
|
||||||
{
|
|
||||||
seconds = 0f;
|
|
||||||
while (physicsTimer - 0.01f > seconds)
|
|
||||||
{
|
|
||||||
physicsTimer -= 0.01f;
|
|
||||||
engine.Step(-.01f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.Space))
|
|
||||||
{
|
|
||||||
seconds += gameTime.ElapsedGameTime.Milliseconds * .005f;
|
|
||||||
while (physicsTimer + 0.01f < seconds)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Physics Timer: {physicsTimer}");
|
|
||||||
physicsTimer += 0.01f;
|
|
||||||
engine.Step(.01f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Keyboard.GetState().IsKeyDown(Keys.B))
|
|
||||||
{
|
|
||||||
seconds -= gameTime.ElapsedGameTime.Milliseconds * .005f;
|
|
||||||
while (physicsTimer - 0.01f > seconds)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Physics Timer: {physicsTimer}");
|
|
||||||
physicsTimer -= 0.01f;
|
|
||||||
engine.Step(-.01f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (physicsTimer + 0.01f < gameTime.TotalGameTime.TotalMilliseconds * .001f)//seconds)
|
|
||||||
{
|
|
||||||
// Console.WriteLine($"Physics Timer: {physicsTimer}");
|
|
||||||
physicsTimer += 0.01f;
|
|
||||||
engine.Step(.01f);
|
|
||||||
}
|
|
||||||
gameManager.Update(gameTime.ToEngineTime());
|
|
||||||
base.Update(gameTime);
|
|
||||||
}
|
|
||||||
static float physicsTimer = 0f;
|
|
||||||
static float seconds = 0f;
|
|
||||||
|
|
||||||
protected override void Draw(GameTime gameTime)
|
|
||||||
{
|
|
||||||
GraphicsDevice.Clear(new Color() { R = 32, G = 32, B = 32 });
|
|
||||||
|
|
||||||
// gameManager.Camera.Position = gameObjectBall.Transform.Position;
|
|
||||||
// Console.WriteLine($"Pos: {gameManager.Camera.Position}");
|
|
||||||
|
|
||||||
// TODO: Add your drawing code here
|
|
||||||
gameManager.PreDraw();
|
|
||||||
gameManager.Camera.Update();
|
|
||||||
|
|
||||||
_spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cameraBehaviour.MatrixTransform);
|
|
||||||
foreach (IGameObject gameObject in gameManager)
|
|
||||||
foreach (var displayable in gameObject.BehaviourController.GetBehaviours<IDisplayable>())
|
|
||||||
displayable.Draw(_spriteBatch);
|
|
||||||
_spriteBatch.End();
|
|
||||||
|
|
||||||
_shapeBatch.Begin(cameraBehaviour.MatrixTransform);
|
|
||||||
foreach (IGameObject gameObject in gameManager)
|
|
||||||
foreach (var displayableShape in gameObject.BehaviourController.GetBehaviours<IDisplayableShape>())
|
|
||||||
displayableShape.Draw(_shapeBatch);
|
|
||||||
_shapeBatch.End();
|
|
||||||
|
|
||||||
base.Draw(gameTime);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ISprite"/> field.
|
|
||||||
/// </summary>
|
|
||||||
public interface IAssignableSprite : IAssignable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Callback triggered when the <see cref="ISprite"/> value has has been assigned a new value.
|
|
||||||
/// </summary>
|
|
||||||
Action<IAssignableSprite>? OnSpriteAssigned { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ISprite" />
|
|
||||||
ISprite Sprite { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Assign a value to the <see cref="ISprite"/> field of this object
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sprite">New <see cref="ISprite"/> to assign.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
|
||||||
/// </returns>
|
|
||||||
bool Assign(ISprite sprite);
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
// TODO Probably gonna have to rethink this
|
|
||||||
public interface ISprite
|
|
||||||
{
|
|
||||||
Action<ISprite>? OnTextureChanged { get; set; }
|
|
||||||
|
|
||||||
Texture2D Texture2D { get; set; }
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
public class Sprite : ISprite
|
|
||||||
{
|
|
||||||
public Action<ISprite>? OnTextureChanged { get; set; }
|
|
||||||
|
|
||||||
private Texture2D _texture = null!;
|
|
||||||
|
|
||||||
public Texture2D Texture2D
|
|
||||||
{
|
|
||||||
get => _texture;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_texture == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_texture = value;
|
|
||||||
OnTextureChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
public interface IDisplayable
|
|
||||||
{
|
|
||||||
public void Draw(SpriteBatch spriteBatch);
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Graphics.TwoDimensional.Abstract;
|
|
||||||
public interface IDisplayableSprite : IDisplayable, IAssignableSprite
|
|
||||||
{
|
|
||||||
Action<IDisplayableSprite>? OnSpriteEffectsChanged { get; set; }
|
|
||||||
Action<IDisplayableSprite>? OnOriginChanged { get; set; }
|
|
||||||
Action<IDisplayableSprite>? OnColorChanged { get; set; }
|
|
||||||
Action<IDisplayableSprite>? OnDepthChanged { get; set; }
|
|
||||||
|
|
||||||
SpriteEffects SpriteEffects { get; set; }
|
|
||||||
Vector2 Origin { get; set; }
|
|
||||||
Color Color { get; set; }
|
|
||||||
float Depth { get; set; }
|
|
||||||
}
|
|
@@ -1,117 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core;
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Graphics.TwoDimensional.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Graphics.TwoDimensional;
|
|
||||||
|
|
||||||
public class DisplayableSpriteBehaviour : Behaviour, IDisplayableSprite, IAssignableSprite
|
|
||||||
{
|
|
||||||
public Action<IAssignableSprite>? OnSpriteAssigned { get; set; } = null;
|
|
||||||
public Action<IDisplayableSprite>? OnSpriteEffectsChanged { get; set; } = null;
|
|
||||||
public Action<IDisplayableSprite>? OnOriginChanged { get; set; } = null;
|
|
||||||
public Action<IDisplayableSprite>? OnColorChanged { get; set; } = null;
|
|
||||||
public Action<IDisplayableSprite>? OnDepthChanged { get; set; } = null;
|
|
||||||
|
|
||||||
private ISprite _sprite = null!;
|
|
||||||
private Color _color = Color.White;
|
|
||||||
private float _depth = 0f;
|
|
||||||
private SpriteEffects _spriteEffects = SpriteEffects.None;
|
|
||||||
private Vector2 _origin = Vector2.One * .5f;
|
|
||||||
|
|
||||||
|
|
||||||
public ISprite Sprite => _sprite;
|
|
||||||
|
|
||||||
public SpriteEffects SpriteEffects
|
|
||||||
{
|
|
||||||
get => _spriteEffects;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_spriteEffects == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_spriteEffects = value;
|
|
||||||
OnSpriteEffectsChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 Origin
|
|
||||||
{
|
|
||||||
get => _origin;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_origin == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_origin = value;
|
|
||||||
OnOriginChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color Color
|
|
||||||
{
|
|
||||||
get => _color;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_color == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_color = value;
|
|
||||||
OnColorChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Depth
|
|
||||||
{
|
|
||||||
get => _depth;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_depth == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_depth = value;
|
|
||||||
OnDepthChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(SpriteBatch spriteBatch)
|
|
||||||
{
|
|
||||||
if (!BehaviourController.GameObject.StateEnable.Enabled || !StateEnable.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ITransform transform = BehaviourController.GameObject.Transform;
|
|
||||||
Vector2D position = transform.Position;
|
|
||||||
Vector2D scale = transform.Scale;
|
|
||||||
|
|
||||||
Rectangle rectangle = new((int)position.X, -(int)position.Y, (int)(Sprite.Texture2D.Width * scale.X), (int)(Sprite.Texture2D.Height * scale.Y));
|
|
||||||
|
|
||||||
spriteBatch.Draw(Sprite.Texture2D, rectangle, null, Color, transform.Rotation, new Vector2(Sprite.Texture2D.Width, Sprite.Texture2D.Height) * Origin, SpriteEffects, Depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Assign(ISprite sprite)
|
|
||||||
{
|
|
||||||
_sprite = sprite;
|
|
||||||
OnSpriteAssigned?.Invoke(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayableSpriteBehaviour() => OnUnassigned += OnUnassign;
|
|
||||||
public DisplayableSpriteBehaviour(
|
|
||||||
Color? color = null,
|
|
||||||
float? depth = null,
|
|
||||||
SpriteEffects? spriteEffects = null,
|
|
||||||
Vector2? origin = null)
|
|
||||||
{
|
|
||||||
OnUnassigned += OnUnassign;
|
|
||||||
|
|
||||||
_color = color ?? _color;
|
|
||||||
_depth = depth ?? _depth;
|
|
||||||
_spriteEffects = spriteEffects ?? _spriteEffects;
|
|
||||||
_origin = origin ?? _origin;
|
|
||||||
}
|
|
||||||
private void OnUnassign(IAssignable assignable) => _sprite = null!;
|
|
||||||
}
|
|
BIN
Game/Icon.bmp
Before Width: | Height: | Size: 256 KiB |
BIN
Game/Icon.ico
Before Width: | Height: | Size: 144 KiB |
@@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
using var game = new Pong.Game1();
|
|
||||||
game.Run();
|
|
@@ -3,31 +3,31 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"dotnet-mgcb": {
|
"dotnet-mgcb": {
|
||||||
"version": "3.8.1.303",
|
"version": "3.8.2.1105",
|
||||||
"commands": [
|
"commands": [
|
||||||
"mgcb"
|
"mgcb"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dotnet-mgcb-editor": {
|
"dotnet-mgcb-editor": {
|
||||||
"version": "3.8.1.303",
|
"version": "3.8.2.1105",
|
||||||
"commands": [
|
"commands": [
|
||||||
"mgcb-editor"
|
"mgcb-editor"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dotnet-mgcb-editor-linux": {
|
"dotnet-mgcb-editor-linux": {
|
||||||
"version": "3.8.1.303",
|
"version": "3.8.2.1105",
|
||||||
"commands": [
|
"commands": [
|
||||||
"mgcb-editor-linux"
|
"mgcb-editor-linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dotnet-mgcb-editor-windows": {
|
"dotnet-mgcb-editor-windows": {
|
||||||
"version": "3.8.1.303",
|
"version": "3.8.2.1105",
|
||||||
"commands": [
|
"commands": [
|
||||||
"mgcb-editor-windows"
|
"mgcb-editor-windows"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dotnet-mgcb-editor-mac": {
|
"dotnet-mgcb-editor-mac": {
|
||||||
"version": "3.8.1.303",
|
"version": "3.8.2.1105",
|
||||||
"commands": [
|
"commands": [
|
||||||
"mgcb-editor-mac"
|
"mgcb-editor-mac"
|
||||||
]
|
]
|
11
Platforms/Desktop/ClientConfiguration.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
public class ClientConfiguration
|
||||||
|
{
|
||||||
|
public bool Host = false;
|
||||||
|
public int HostPort = 8888;
|
||||||
|
|
||||||
|
public string ServerAddress = "localhost";
|
||||||
|
public int ServerPort = 8888;
|
||||||
|
|
||||||
|
public int windowWidth = 1024;
|
||||||
|
public int windowHeight = 576;
|
||||||
|
}
|
15
Platforms/Desktop/Content/Content.mgcb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
#----------------------------- Global Properties ----------------------------#
|
||||||
|
|
||||||
|
/outputDir:bin/$(Platform)
|
||||||
|
/intermediateDir:obj/$(Platform)
|
||||||
|
/platform:DesktopGL
|
||||||
|
/config:
|
||||||
|
/profile:Reach
|
||||||
|
/compress:False
|
||||||
|
|
||||||
|
#-------------------------------- References --------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------- Content ---------------------------------#
|
||||||
|
|
90
Platforms/Desktop/Desktop.csproj
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<RollForward>Major</RollForward>
|
||||||
|
<PublishReadyToRun>false</PublishReadyToRun>
|
||||||
|
<TieredCompilation>false</TieredCompilation>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>Pong.Platforms.Desktop</RootNamespace>
|
||||||
|
<AssemblyName>Pong</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Icon.ico" />
|
||||||
|
<None Remove="Icon.bmp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Icon.ico">
|
||||||
|
<LogicalName>Icon.ico</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Icon.bmp">
|
||||||
|
<LogicalName>Icon.bmp</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.2.1105" />
|
||||||
|
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.2.1105" />
|
||||||
|
<PackageReference Include="nulastudio.NetBeauty" Version="2.1.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Shared/Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<MonoGameContentReference Include="../../Shared/Content/Content.mgcb">
|
||||||
|
<Link>Content/Content.mgcb</Link>
|
||||||
|
</MonoGameContentReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Shared/Shared.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Engine\Engine.Serializers\Engine.Serializers.Yaml\Engine.Serializers.Yaml.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
|
||||||
|
<Message Text="Restoring dotnet tools" Importance="High" />
|
||||||
|
<Exec Command="dotnet tool restore" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<BeautySharedRuntimeMode>False</BeautySharedRuntimeMode>
|
||||||
|
<!-- beauty into sub-directory, default is libs, quote with "" if contains space -->
|
||||||
|
<BeautyLibsDir Condition="$(BeautySharedRuntimeMode) == 'True'">../Libraries</BeautyLibsDir>
|
||||||
|
<BeautyLibsDir Condition="$(BeautySharedRuntimeMode) != 'True'">./Libraries</BeautyLibsDir>
|
||||||
|
<!-- dlls that you don't want to be moved or can not be moved -->
|
||||||
|
<!-- <BeautyExcludes>dll1.dll;lib*;...</BeautyExcludes> -->
|
||||||
|
<BeautyExcludes>SDL2.dll</BeautyExcludes>
|
||||||
|
<!-- dlls that end users never needed, so hide them -->
|
||||||
|
<BeautyHiddens>hostfxr;hostpolicy;*.deps.json;*.runtimeconfig*.json</BeautyHiddens>
|
||||||
|
<!-- set to True if you want to disable -->
|
||||||
|
<DisableBeauty>False</DisableBeauty>
|
||||||
|
<!-- set to False if you want to beauty on build -->
|
||||||
|
<BeautyOnPublishOnly>False</BeautyOnPublishOnly>
|
||||||
|
<!-- DO NOT TOUCH THIS OPTION -->
|
||||||
|
<BeautyNoRuntimeInfo>False</BeautyNoRuntimeInfo>
|
||||||
|
<!-- valid values: auto|with|without -->
|
||||||
|
<BeautyNBLoaderVerPolicy>auto</BeautyNBLoaderVerPolicy>
|
||||||
|
<!-- set to True if you want to allow 3rd debuggers(like dnSpy) debugs the app -->
|
||||||
|
<BeautyEnableDebugging>False</BeautyEnableDebugging>
|
||||||
|
<!-- the patch can reduce the file count -->
|
||||||
|
<!-- set to False if you want to disable -->
|
||||||
|
<!-- SCD Mode Feature Only -->
|
||||||
|
<BeautyUsePatch>True</BeautyUsePatch>
|
||||||
|
<!-- App Entry Dll = BeautyDir + BeautyAppHostDir + BeautyAppHostEntry -->
|
||||||
|
<!-- see https://github.com/nulastudio/NetBeauty2#customize-apphost for more details -->
|
||||||
|
<!-- relative path based on AppHostDir -->
|
||||||
|
<!-- .NET Core Non Single-File Only -->
|
||||||
|
<!-- <BeautyAppHostEntry>bin/MyApp.dll</BeautyAppHostEntry> -->
|
||||||
|
<!-- relative path based on BeautyDir -->
|
||||||
|
<!-- .NET Core Non Single-File Only -->
|
||||||
|
<!-- <BeautyAppHostDir>..</BeautyAppHostDir> -->
|
||||||
|
<!-- <BeautyAfterTasks></BeautyAfterTasks> -->
|
||||||
|
<!-- valid values: Error|Detail|Info -->
|
||||||
|
<BeautyLogLevel>Info</BeautyLogLevel>
|
||||||
|
<!-- set to a repo mirror if you have troble in connecting github -->
|
||||||
|
<!-- <BeautyGitCDN>https://gitee.com/liesauer/HostFXRPatcher</BeautyGitCDN> -->
|
||||||
|
<!-- <BeautyGitTree>master</BeautyGitTree> -->
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
BIN
Platforms/Desktop/Icon.bmp
Normal file
After Width: | Height: | Size: 192 KiB |
BIN
Platforms/Desktop/Icon.ico
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
Platforms/Desktop/Icon.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
59
Platforms/Desktop/Program.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
using Syntriax.Engine.Core.Serialization;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
using Syntriax.Engine.Serializers.Yaml;
|
||||||
|
|
||||||
|
Universe universe = new();
|
||||||
|
|
||||||
|
ISerializer serializer = new YamlSerializer();
|
||||||
|
|
||||||
|
ILogger logger = new FileLogger($"Logs/{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss-ffffff}.log");
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
logger = new LoggerWrapper(logger, new ConsoleLogger());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Logger").BehaviourController.AddBehaviour<LoggerContainer>().Logger = logger;
|
||||||
|
|
||||||
|
string settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.yaml");
|
||||||
|
ClientConfiguration clientConfiguration;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
clientConfiguration = serializer.Deserialize<ClientConfiguration>(File.ReadAllText(settingsPath));
|
||||||
|
logger.Log(clientConfiguration, $"{settingsPath} read");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
clientConfiguration = new();
|
||||||
|
logger.LogError(serializer, $"Error loading {settingsPath}. Creating new settings file");
|
||||||
|
logger.LogException(serializer, exception);
|
||||||
|
File.WriteAllText(settingsPath, serializer.Serialize(clientConfiguration));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientConfiguration.Host)
|
||||||
|
Pong.PongUniverse.ApplyPongServer(universe, clientConfiguration.HostPort);
|
||||||
|
|
||||||
|
Pong.PongUniverse.ApplyPongClient(universe, clientConfiguration.ServerAddress, clientConfiguration.ServerPort);
|
||||||
|
Pong.PongUniverse.ApplyPongUniverse(universe);
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Desktop HO")
|
||||||
|
.BehaviourController.AddBehaviour<KeyboardInputs>();
|
||||||
|
|
||||||
|
using MonoGameWindow monoGameWindow = new(universe);
|
||||||
|
|
||||||
|
monoGameWindow.Graphics.PreferredBackBufferWidth = clientConfiguration.windowWidth;
|
||||||
|
monoGameWindow.Graphics.PreferredBackBufferHeight = clientConfiguration.windowHeight;
|
||||||
|
monoGameWindow.Graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||||
|
|
||||||
|
universe.FindBehaviour<Syntriax.Engine.Network.INetworkCommunicatorClient>()?
|
||||||
|
.OnConnectionEstablished.AddOneTimeListener(
|
||||||
|
(sender, connection) => monoGameWindow.Window.Title = $"Client {connection.Id}"
|
||||||
|
);
|
||||||
|
|
||||||
|
monoGameWindow.Run();
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<assemblyIdentity version="1.0.0.0" name="Game"/>
|
<assemblyIdentity version="1.0.0.0" name="Desktop"/>
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
131
Platforms/Desktop/icon.svg
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="100mm"
|
||||||
|
height="100mm"
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="icon.svg"
|
||||||
|
inkscape:export-filename="Icon.png"
|
||||||
|
inkscape:export-xdpi="65.024002"
|
||||||
|
inkscape:export-ydpi="65.024002"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.76791526"
|
||||||
|
inkscape:cx="-14.975611"
|
||||||
|
inkscape:cy="272.16545"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1008"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="36"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="taper_stroke"
|
||||||
|
id="path-effect1965"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
stroke_width="14.57"
|
||||||
|
attach_start="0.99999"
|
||||||
|
end_offset="1e-07"
|
||||||
|
start_smoothing="0"
|
||||||
|
end_smoothing="0"
|
||||||
|
jointype="extrapolated"
|
||||||
|
start_shape="center"
|
||||||
|
end_shape="center"
|
||||||
|
miter_limit="1000.5" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="join_type"
|
||||||
|
id="path-effect1959"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
linecap_type="square"
|
||||||
|
line_width="0.26458299"
|
||||||
|
linejoin_type="extrp_arc"
|
||||||
|
miter_limit="100"
|
||||||
|
attempt_force_join="true" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="powerstroke"
|
||||||
|
id="path-effect1957"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
offset_points="0.2,0.1322915 | 1,0.1322915 | 1.8,0.1322915"
|
||||||
|
not_jump="false"
|
||||||
|
sort_points="true"
|
||||||
|
interpolator_type="Linear"
|
||||||
|
interpolator_beta="1"
|
||||||
|
start_linecap_type="zerowidth"
|
||||||
|
linejoin_type="miter"
|
||||||
|
miter_limit="11100"
|
||||||
|
scale_width="20"
|
||||||
|
end_linecap_type="butt" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fill_between_many"
|
||||||
|
method="bsplinespiro"
|
||||||
|
linkedpaths="#path1175,0,1"
|
||||||
|
id="path-effect1961"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="0"
|
||||||
|
join="true"
|
||||||
|
close="true"
|
||||||
|
autoreverse="true" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fill_between_many"
|
||||||
|
method="bsplinespiro"
|
||||||
|
linkedpaths="#path1175,0,1"
|
||||||
|
id="path-effect1967"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="0"
|
||||||
|
join="true"
|
||||||
|
close="true"
|
||||||
|
autoreverse="true" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#25202c;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:square;stroke-linejoin:bevel;stroke-opacity:1;paint-order:fill markers stroke"
|
||||||
|
id="rect4712"
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
ry="24.342688" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.296213;stroke-linecap:square;stroke-linejoin:bevel;paint-order:fill markers stroke"
|
||||||
|
id="path234"
|
||||||
|
cx="32.183838"
|
||||||
|
cy="37.277283"
|
||||||
|
r="11.198684" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.296213;stroke-linecap:square;stroke-linejoin:bevel;paint-order:fill markers stroke"
|
||||||
|
id="rect1054"
|
||||||
|
width="19.398724"
|
||||||
|
height="71.576004"
|
||||||
|
x="59.616123"
|
||||||
|
y="14.211996" />
|
||||||
|
<path
|
||||||
|
id="path1175"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.31583;stroke-linecap:square;stroke-linejoin:bevel;stroke-opacity:1;paint-order:fill markers stroke"
|
||||||
|
d="m 45.710924,37.909235 c -0.08308,1.7843 -0.506133,3.552079 -1.278577,5.16464 -1.067261,2.404981 -2.906892,4.366553 -5.129349,5.714834 l 10.696654,4.40096 -13.418115,24.765056 21.254461,-19.595787 c 5.44e-4,-4.297076 -4.96e-4,-8.593943 5.78e-4,-12.891057 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
39
Platforms/Server/PongEndpoints.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
namespace Server;
|
||||||
|
|
||||||
|
public class PongEndpoints : Behaviour, IFirstFrameUpdate
|
||||||
|
{
|
||||||
|
private INetworkCommunicatorServer? server = null!;
|
||||||
|
|
||||||
|
public PongEndpoints()
|
||||||
|
{
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||||
|
|
||||||
|
builder.Services.AddHealthChecks();
|
||||||
|
|
||||||
|
WebApplication app = builder.Build();
|
||||||
|
|
||||||
|
app.MapHealthChecks("/health");
|
||||||
|
|
||||||
|
app.MapGet("/stats", GetStats);
|
||||||
|
|
||||||
|
app.Run($"http://0.0.0.0:{Environment.GetEnvironmentVariable("PORT") ?? "8888"}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IResult GetStats() => Results.Json(new { Count = server?.Connections.Count ?? 0 });
|
||||||
|
|
||||||
|
public void FirstActiveFrame() => server = Universe.FindRequiredBehaviour<INetworkCommunicatorServer>();
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe) => server = null;
|
||||||
|
}
|
34
Platforms/Server/Program.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
Universe universe = new();
|
||||||
|
|
||||||
|
FileLogger fileLogger = new($"Logs/{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss-ffffff}.log");
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Logger").BehaviourController
|
||||||
|
.AddBehaviour<LoggerContainer>().Logger = new LoggerWrapper(fileLogger, new ConsoleLogger());
|
||||||
|
|
||||||
|
Pong.PongUniverse.ApplyPongServer(universe, int.Parse(Environment.GetEnvironmentVariable("PORT") ?? "8888"));
|
||||||
|
Pong.PongUniverse.ApplyPongUniverse(universe);
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Endpoints").BehaviourController
|
||||||
|
.AddBehaviour<Server.PongEndpoints>();
|
||||||
|
|
||||||
|
DateTime lastRun = DateTime.UtcNow;
|
||||||
|
TimeSpan interval = new(0, 0, 0, 0, 16);
|
||||||
|
TimeSpan timeSinceStart = new(0);
|
||||||
|
|
||||||
|
universe.Initialize();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (lastRun + interval <= DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
lastRun += interval;
|
||||||
|
timeSinceStart += interval;
|
||||||
|
universe.Update(new(timeSinceStart, interval));
|
||||||
|
}
|
||||||
|
Thread.Sleep(1);
|
||||||
|
}
|
20
Platforms/Server/Server.csproj
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Shared/Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.3.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.0-preview.5.25277.114" />
|
||||||
|
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.2.1105" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
151
Pong.sln
@@ -7,41 +7,160 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{F7F626
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "Engine\Engine.Core\Engine.Core.csproj", "{990CA10C-1EBB-4395-A43A-456B7029D8C9}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "Engine\Engine.Core\Engine.Core.csproj", "{990CA10C-1EBB-4395-A43A-456B7029D8C9}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game", "Game\Game.csproj", "{500E1B05-39D7-4232-8051-E7351D745306}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine\Engine.Input\Engine.Input.csproj", "{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine\Engine.Physics2D\Engine.Physics2D.csproj", "{0D97F83C-B043-48B1-B155-7354C4E84FC0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine\Engine.Physics2D\Engine.Physics2D.csproj", "{0D97F83C-B043-48B1-B155-7354C4E84FC0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine\Engine.csproj", "{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Platforms", "Platforms", "{FECFFD54-338F-4060-9161-1E5770D1DC33}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Platforms\Desktop\Desktop.csproj", "{2B627F66-5A61-4F69-B479-62EEAB603D01}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Systems", "Engine\Engine.Systems\Engine.Systems.csproj", "{8863A1BA-2E83-419F-BACB-D4A4156EC71C}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Integration", "Engine.Integration", "{9059393F-4073-9273-0EEC-2B1BA61B620B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.MonoGame", "Engine\Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Platforms\Server\Server.csproj", "{A15263DB-DF65-4A07-8CA1-33A2919501A0}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Serializers", "Engine.Serializers", "{F1257AE1-A8BC-DE44-CB86-406F1335A1D0}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Serializers.Yaml", "Engine\Engine.Serializers\Engine.Serializers.Yaml\Engine.Serializers.Yaml.csproj", "{79F870AB-249E-4CA0-9DF0-F265514581DF}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
Release|x64 = Release|x64
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
Release|x86 = Release|x86
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{500E1B05-39D7-4232-8051-E7351D745306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
{500E1B05-39D7-4232-8051-E7351D745306}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{500E1B05-39D7-4232-8051-E7351D745306}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{500E1B05-39D7-4232-8051-E7351D745306}.Release|Any CPU.Build.0 = Release|Any CPU
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
{990CA10C-1EBB-4395-A43A-456B7029D8C9} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
|
||||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
{0D97F83C-B043-48B1-B155-7354C4E84FC0} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||||
|
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||||
|
{2B627F66-5A61-4F69-B479-62EEAB603D01} = {FECFFD54-338F-4060-9161-1E5770D1DC33}
|
||||||
|
{8863A1BA-2E83-419F-BACB-D4A4156EC71C} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||||
|
{9059393F-4073-9273-0EEC-2B1BA61B620B} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||||
|
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283} = {9059393F-4073-9273-0EEC-2B1BA61B620B}
|
||||||
|
{A15263DB-DF65-4A07-8CA1-33A2919501A0} = {FECFFD54-338F-4060-9161-1E5770D1DC33}
|
||||||
|
{F1257AE1-A8BC-DE44-CB86-406F1335A1D0} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||||
|
{79F870AB-249E-4CA0-9DF0-F265514581DF} = {F1257AE1-A8BC-DE44-CB86-406F1335A1D0}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
36
Shared/.config/dotnet-tools.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"dotnet-mgcb": {
|
||||||
|
"version": "3.8.2.1105",
|
||||||
|
"commands": [
|
||||||
|
"mgcb"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor": {
|
||||||
|
"version": "3.8.2.1105",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor-linux": {
|
||||||
|
"version": "3.8.2.1105",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor-linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor-windows": {
|
||||||
|
"version": "3.8.2.1105",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor-windows"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor-mac": {
|
||||||
|
"version": "3.8.2.1105",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor-mac"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
Shared/Behaviours/Ball.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Network;
|
||||||
|
using Syntriax.Engine.Physics2D;
|
||||||
|
using Syntriax.Engine.Systems.Tween;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class Ball : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEntity,
|
||||||
|
IPacketListenerClient<Ball.BallUpdatePacket>,
|
||||||
|
IPacketListenerClient<Ball.BallResetPacket>
|
||||||
|
{
|
||||||
|
public float Speed { get; set; } = 500f;
|
||||||
|
public float SpeedUpMultiplier { get; set; } = .025f;
|
||||||
|
|
||||||
|
public IRigidBody2D RigidBody { get; private set; } = null!;
|
||||||
|
|
||||||
|
private IPhysicsEngine2D physicsEngine2D = null!;
|
||||||
|
private ITweenManager tweenManager = null!;
|
||||||
|
private INetworkCommunicatorServer? networkServer = null;
|
||||||
|
private ITween? networkTween = null;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
BehaviourController.GetRequiredBehaviour<ICollider2D>().OnCollisionDetected.AddListener(OnCollisionDetected);
|
||||||
|
physicsEngine2D = Universe.FindRequiredBehaviour<IPhysicsEngine2D>();
|
||||||
|
tweenManager = Universe.FindRequiredBehaviour<ITweenManager>();
|
||||||
|
RigidBody = BehaviourController.GetRequiredBehaviour<IRigidBody2D>();
|
||||||
|
networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LaunchBall(Vector2D launchDirection)
|
||||||
|
{
|
||||||
|
ResetBall();
|
||||||
|
RigidBody.Velocity = launchDirection * Speed;
|
||||||
|
networkServer?.SendToAll(new BallUpdatePacket(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetBall()
|
||||||
|
{
|
||||||
|
if (networkTween is not null)
|
||||||
|
tweenManager.CancelTween(networkTween);
|
||||||
|
Transform.Position = Vector2D.Zero;
|
||||||
|
RigidBody.Velocity = Vector2D.Zero;
|
||||||
|
networkServer?.SendToAll(new BallResetPacket());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PhysicsUpdate(float delta)
|
||||||
|
{
|
||||||
|
if (RigidBody.Velocity.MagnitudeSquared <= 0.01f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector2D direction = RigidBody.Velocity.Normalized;
|
||||||
|
RigidBody.Velocity += direction * delta * SpeedUpMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCollisionDetected(ICollider2D collider2D, CollisionDetectionInformation information)
|
||||||
|
{
|
||||||
|
if (Math.Abs(information.Normal.Dot(Vector2D.Right)) > .25)
|
||||||
|
RigidBody.Velocity = information.Detected.Transform.Position.FromTo(information.Detector.Transform.Position).Normalized * RigidBody.Velocity.Magnitude;
|
||||||
|
else
|
||||||
|
RigidBody.Velocity = RigidBody.Velocity.Reflect(information.Normal);
|
||||||
|
networkServer?.SendToAll(new BallUpdatePacket(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ball() { }
|
||||||
|
public Ball(float speed) => Speed = speed;
|
||||||
|
|
||||||
|
void IPacketListenerClient<BallResetPacket>.OnClientPacketArrived(IConnection sender, BallResetPacket packet) => ResetBall();
|
||||||
|
void IPacketListenerClient<BallUpdatePacket>.OnClientPacketArrived(IConnection sender, BallUpdatePacket packet)
|
||||||
|
{
|
||||||
|
Vector2D localToServerPosition = Transform.Position.FromTo(packet.Position);
|
||||||
|
if (localToServerPosition.MagnitudeSquared < 4f)
|
||||||
|
networkTween = Transform.TweenPositionAdditive(tweenManager, .25f, localToServerPosition);
|
||||||
|
else
|
||||||
|
Transform.Position = packet.Position;
|
||||||
|
|
||||||
|
RigidBody.Velocity = packet.Velocity;
|
||||||
|
physicsEngine2D.StepIndividual(RigidBody, sender.Ping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BallResetPacket : INetworkPacket;
|
||||||
|
private class BallUpdatePacket : INetworkPacket
|
||||||
|
{
|
||||||
|
public Vector2D Position { get; set; } = Vector2D.Zero;
|
||||||
|
public Vector2D Velocity { get; set; } = Vector2D.Zero;
|
||||||
|
|
||||||
|
public BallUpdatePacket() { }
|
||||||
|
public BallUpdatePacket(Ball ballBehaviour)
|
||||||
|
{
|
||||||
|
Position = ballBehaviour.Transform.Position;
|
||||||
|
Velocity = ballBehaviour.RigidBody.Velocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
Shared/Behaviours/CameraController.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
using Syntriax.Engine.Systems.Input;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class CameraController : Behaviour, IFirstFrameUpdate, IUpdate
|
||||||
|
{
|
||||||
|
private MonoGameCamera2D cameraBehaviour = null!;
|
||||||
|
private IButtonInputs<Keys> buttonInputs = null!;
|
||||||
|
|
||||||
|
private float defaultZoomLevel = 1f;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
cameraBehaviour = BehaviourController.GetRequiredBehaviour<MonoGameCamera2D>();
|
||||||
|
buttonInputs = Universe.FindRequiredBehaviour<IButtonInputs<Keys>>();
|
||||||
|
|
||||||
|
buttonInputs.RegisterOnPress(Keys.F, SwitchToFullScreen);
|
||||||
|
buttonInputs.RegisterOnPress(Keys.R, ResetCamera);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (buttonInputs.IsPressed(Keys.U))
|
||||||
|
cameraBehaviour.Zoom += Universe.Time.DeltaTime * 5f;
|
||||||
|
if (buttonInputs.IsPressed(Keys.J))
|
||||||
|
cameraBehaviour.Zoom -= Universe.Time.DeltaTime * 5f;
|
||||||
|
|
||||||
|
|
||||||
|
if (buttonInputs.IsPressed(Keys.NumPad8)) cameraBehaviour.Transform.LocalPosition += Vector2D.Up * Universe.Time.DeltaTime * 500f;
|
||||||
|
if (buttonInputs.IsPressed(Keys.NumPad2)) cameraBehaviour.Transform.LocalPosition -= Vector2D.Up * Universe.Time.DeltaTime * 500f;
|
||||||
|
if (buttonInputs.IsPressed(Keys.NumPad6)) cameraBehaviour.Transform.LocalPosition += Vector2D.Right * Universe.Time.DeltaTime * 500f;
|
||||||
|
if (buttonInputs.IsPressed(Keys.NumPad4)) cameraBehaviour.Transform.LocalPosition -= Vector2D.Right * Universe.Time.DeltaTime * 500f;
|
||||||
|
|
||||||
|
|
||||||
|
if (buttonInputs.IsPressed(Keys.Q))
|
||||||
|
cameraBehaviour.Transform.Rotation += Universe.Time.DeltaTime * 45f;
|
||||||
|
if (buttonInputs.IsPressed(Keys.E))
|
||||||
|
cameraBehaviour.Transform.Rotation -= Universe.Time.DeltaTime * 45f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SwitchToFullScreen(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args)
|
||||||
|
{
|
||||||
|
if (cameraBehaviour.Graphics.IsFullScreen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cameraBehaviour.Graphics.PreferMultiSampling = false;
|
||||||
|
cameraBehaviour.Graphics.PreferredBackBufferWidth = cameraBehaviour.Graphics.GraphicsDevice.Adapter.CurrentDisplayMode.Width;
|
||||||
|
cameraBehaviour.Graphics.PreferredBackBufferHeight = cameraBehaviour.Graphics.GraphicsDevice.Adapter.CurrentDisplayMode.Height;
|
||||||
|
cameraBehaviour.Graphics.IsFullScreen = true;
|
||||||
|
cameraBehaviour.Graphics.ApplyChanges();
|
||||||
|
|
||||||
|
float previousScreenSize = Math.Sqrt(Math.Sqr(cameraBehaviour.Viewport.Width) + Math.Sqr(cameraBehaviour.Viewport.Height));
|
||||||
|
float currentScreenSize = Math.Sqrt(Math.Sqr(cameraBehaviour.Graphics.GraphicsDevice.Viewport.Width) + Math.Sqr(cameraBehaviour.Graphics.GraphicsDevice.Viewport.Height));
|
||||||
|
defaultZoomLevel /= previousScreenSize / currentScreenSize;
|
||||||
|
cameraBehaviour.Zoom /= previousScreenSize / currentScreenSize;
|
||||||
|
cameraBehaviour.Viewport = cameraBehaviour.Graphics.GraphicsDevice.Viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetCamera(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args)
|
||||||
|
{
|
||||||
|
cameraBehaviour.Zoom = defaultZoomLevel;
|
||||||
|
cameraBehaviour.Transform.LocalPosition = Vector2D.Zero;
|
||||||
|
cameraBehaviour.Transform.LocalRotation = 0f;
|
||||||
|
}
|
||||||
|
}
|
29
Shared/Behaviours/DrawableColliderCircle.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class DrawableColliderCircle : Syntriax.Engine.Physics2D.Collider2DCircle, IDrawableTriangle
|
||||||
|
{
|
||||||
|
private const float CIRCLE_SEGMENT_COUNT = 32f;
|
||||||
|
|
||||||
|
public ColorRGBA Color { get; set; } = new ColorRGBA(255, 255, 255);
|
||||||
|
|
||||||
|
public void Draw(ITriangleBatch triangleBatch)
|
||||||
|
{
|
||||||
|
Recalculate();
|
||||||
|
|
||||||
|
for (int i = 0; i < CIRCLE_SEGMENT_COUNT; i++)
|
||||||
|
{
|
||||||
|
float iPi1 = i / CIRCLE_SEGMENT_COUNT * 2f * Math.Pi;
|
||||||
|
float iPi2 = (i + 1f).Mod(CIRCLE_SEGMENT_COUNT) / CIRCLE_SEGMENT_COUNT * 2f * Math.Pi;
|
||||||
|
|
||||||
|
Vector2D firstVertex = new Vector2D(Math.Sin(iPi1), Math.Cos(iPi1)) * CircleWorld.Radius;
|
||||||
|
Vector2D secondVertex = new Vector2D(Math.Sin(iPi2), Math.Cos(iPi2)) * CircleWorld.Radius;
|
||||||
|
triangleBatch.Draw(new(CircleWorld.Center, CircleWorld.Center + firstVertex, CircleWorld.Center + secondVertex), Color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableColliderCircle(Circle circle) : base(circle) { }
|
||||||
|
public DrawableColliderCircle(Circle circle, ColorRGBA color) : base(circle) { Color = color; }
|
||||||
|
}
|
26
Shared/Behaviours/DrawableColliderShape.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class DrawableColliderShape : Syntriax.Engine.Physics2D.Collider2DShape, IDrawableTriangle
|
||||||
|
{
|
||||||
|
private readonly IList<Triangle> triangles = [];
|
||||||
|
|
||||||
|
public ColorRGBA Color { get; set; } = new ColorRGBA(255, 255, 255);
|
||||||
|
|
||||||
|
public void Draw(ITriangleBatch triangleBatch)
|
||||||
|
{
|
||||||
|
Recalculate();
|
||||||
|
|
||||||
|
ShapeWorld.ToTrianglesConvex(triangles);
|
||||||
|
|
||||||
|
foreach (Triangle triangle in triangles)
|
||||||
|
triangleBatch.Draw(triangle, Color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableColliderShape(Shape2D shape) : base(shape) { }
|
||||||
|
public DrawableColliderShape(Shape2D shape, ColorRGBA color) : base(shape) { Color = color; }
|
||||||
|
}
|
25
Shared/Behaviours/Label.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class Label : Behaviour2D, IDrawableSprite
|
||||||
|
{
|
||||||
|
public SpriteFont? Font { get; set; } = null;
|
||||||
|
public int Size { get; set; } = 16;
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public void Draw(ISpriteBatch spriteBatch)
|
||||||
|
{
|
||||||
|
if (Font is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
spriteBatch.DrawString(Font, Text, Transform.Position, Color.White, Transform.Rotation, Vector2D.One * .5f, Transform.Scale.Magnitude, SpriteEffects.None, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Label() { }
|
||||||
|
public Label(SpriteFont font) => Font = font;
|
||||||
|
}
|
126
Shared/Behaviours/Paddle.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Network;
|
||||||
|
using Syntriax.Engine.Physics2D;
|
||||||
|
using Syntriax.Engine.Systems.Input;
|
||||||
|
using Syntriax.Engine.Systems.Tween;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class Paddle(Keys Up, Keys Down, float High, float Low, float Speed) : Behaviour2D,
|
||||||
|
IFirstFrameUpdate, IPhysicsIteration, IPostPhysicsUpdate,
|
||||||
|
IPacketListenerServer<Paddle.PaddleKeyStatePacket>, IPacketListenerClient<Paddle.PaddleKeyStatePacket>
|
||||||
|
{
|
||||||
|
private Keys Up { get; } = Up;
|
||||||
|
private Keys Down { get; } = Down;
|
||||||
|
public float High { get; } = High;
|
||||||
|
public float Low { get; } = Low;
|
||||||
|
public float Speed { get; set; } = Speed;
|
||||||
|
|
||||||
|
private bool isUpPressed = false;
|
||||||
|
private bool isDownPressed = false;
|
||||||
|
|
||||||
|
private IButtonInputs<Keys>? inputs = null;
|
||||||
|
private INetworkCommunicatorClient? networkClient = null!;
|
||||||
|
private INetworkCommunicatorServer? networkServer = null;
|
||||||
|
private IRigidBody2D rigidBody = null!;
|
||||||
|
private IPhysicsEngine2D physicsEngine2D = null!;
|
||||||
|
|
||||||
|
private ITween? networkTween = null;
|
||||||
|
private ITweenManager tweenManager = null!;
|
||||||
|
|
||||||
|
public void PhysicsIterate(float delta)
|
||||||
|
{
|
||||||
|
if (isUpPressed)
|
||||||
|
rigidBody.Transform.Position += Vector2D.Up * Speed * delta;
|
||||||
|
else if (isDownPressed)
|
||||||
|
rigidBody.Transform.Position -= Vector2D.Up * Speed * delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PostPhysicsUpdate(float delta)
|
||||||
|
{
|
||||||
|
Transform.Position = new Vector2D(Transform.Position.X, MathF.Max(MathF.Min(Transform.Position.Y, High), Low));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
physicsEngine2D = Universe.FindRequiredBehaviour<IPhysicsEngine2D>();
|
||||||
|
inputs = Universe.FindBehaviour<IButtonInputs<Keys>>();
|
||||||
|
networkClient = Universe.FindBehaviour<INetworkCommunicatorClient>();
|
||||||
|
networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
|
||||||
|
rigidBody = BehaviourController.GetRequiredBehaviour<IRigidBody2D>();
|
||||||
|
tweenManager = Universe.FindRequiredBehaviour<ITweenManager>();
|
||||||
|
|
||||||
|
inputs?.RegisterOnPress(Up, OnUpPressed);
|
||||||
|
inputs?.RegisterOnRelease(Up, OnUpReleased);
|
||||||
|
|
||||||
|
inputs?.RegisterOnPress(Down, OnDownPressed);
|
||||||
|
inputs?.RegisterOnRelease(Down, OnDownReleased);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFinalize()
|
||||||
|
{
|
||||||
|
inputs?.UnregisterOnPress(Up, OnUpPressed);
|
||||||
|
inputs?.UnregisterOnRelease(Up, OnUpReleased);
|
||||||
|
|
||||||
|
inputs?.UnregisterOnPress(Down, OnDownPressed);
|
||||||
|
inputs?.UnregisterOnRelease(Down, OnDownReleased);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpPressed(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args) { isUpPressed = true; networkClient?.SendToServer(new PaddleKeyStatePacket(this)); }
|
||||||
|
private void OnUpReleased(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args) { isUpPressed = false; networkClient?.SendToServer(new PaddleKeyStatePacket(this)); }
|
||||||
|
private void OnDownPressed(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args) { isDownPressed = true; networkClient?.SendToServer(new PaddleKeyStatePacket(this)); }
|
||||||
|
private void OnDownReleased(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args) { isDownPressed = false; networkClient?.SendToServer(new PaddleKeyStatePacket(this)); }
|
||||||
|
|
||||||
|
public void OnServerPacketArrived(IConnection sender, PaddleKeyStatePacket packet)
|
||||||
|
{
|
||||||
|
physicsEngine2D.StepIndividual(rigidBody, -sender.Ping.Min(.05f));
|
||||||
|
|
||||||
|
isUpPressed = packet.IsUpPressed;
|
||||||
|
isDownPressed = packet.IsDownPressed;
|
||||||
|
|
||||||
|
physicsEngine2D.StepIndividual(rigidBody, sender.Ping.Min(.05f));
|
||||||
|
networkServer?.SendToAll(new PaddleKeyStatePacket(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnClientPacketArrived(IConnection sender, PaddleKeyStatePacket packet)
|
||||||
|
{
|
||||||
|
if (packet.IsDownPressed || packet.IsUpPressed) // Check if the server paddle is moving
|
||||||
|
if (isDownPressed == packet.IsDownPressed && isUpPressed == packet.IsUpPressed) // Check if we are the ones giving the inputs
|
||||||
|
return;
|
||||||
|
|
||||||
|
physicsEngine2D.StepIndividual(rigidBody, -sender.Ping);
|
||||||
|
|
||||||
|
isUpPressed = packet.IsUpPressed;
|
||||||
|
isDownPressed = packet.IsDownPressed;
|
||||||
|
|
||||||
|
physicsEngine2D.StepIndividual(rigidBody, sender.Ping);
|
||||||
|
|
||||||
|
Vector2D localToServerPosition = Transform.Position.FromTo(packet.Position);
|
||||||
|
networkTween = Transform.TweenPositionAdditive(tweenManager, .1f, localToServerPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PaddleKeyStatePacket : IEntityNetworkPacket
|
||||||
|
{
|
||||||
|
public string EntityId { get; set; } = default!;
|
||||||
|
public Vector2D Position { get; set; } = default!;
|
||||||
|
public bool IsUpPressed { get; set; } = default!;
|
||||||
|
public bool IsDownPressed { get; set; } = default!;
|
||||||
|
|
||||||
|
public PaddleKeyStatePacket Set(Paddle paddleBehaviour)
|
||||||
|
{
|
||||||
|
EntityId = paddleBehaviour.Id;
|
||||||
|
Position = paddleBehaviour.Transform.Position;
|
||||||
|
IsUpPressed = paddleBehaviour.isUpPressed;
|
||||||
|
IsDownPressed = paddleBehaviour.isDownPressed;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaddleKeyStatePacket() { }
|
||||||
|
public PaddleKeyStatePacket(Paddle paddleBehaviour) => Set(paddleBehaviour);
|
||||||
|
}
|
||||||
|
}
|
134
Shared/Behaviours/PongManager.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
using Syntriax.Engine.Network;
|
||||||
|
using Syntriax.Engine.Systems.Input;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
|
||||||
|
IPacketListenerServer<PongManager.RequestStartPacket>,
|
||||||
|
IPacketListenerClient<PongManager.ScorePacket>
|
||||||
|
{
|
||||||
|
public Action<PongManager>? OnReset { get; set; } = null;
|
||||||
|
public Action<PongManager>? OnFinished { get; set; } = null;
|
||||||
|
public Action<PongManager>? OnScoreUpdated { get; set; } = null;
|
||||||
|
|
||||||
|
private Random random = new();
|
||||||
|
private Ball ball = null!;
|
||||||
|
|
||||||
|
private INetworkCommunicatorClient? networkClient = null!;
|
||||||
|
private INetworkCommunicatorServer? networkServer = null;
|
||||||
|
private ILogger? logger = null;
|
||||||
|
|
||||||
|
public int ScoreLeft { get; private set; } = 0;
|
||||||
|
public int ScoreRight { get; private set; } = 0;
|
||||||
|
public int ScoreSum => ScoreLeft + ScoreRight;
|
||||||
|
|
||||||
|
public int WinScore { get; } = 5;
|
||||||
|
|
||||||
|
public PongManager() => WinScore = 5;
|
||||||
|
public PongManager(int winScore) => WinScore = winScore;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
IButtonInputs<Keys>? buttonInputs = Universe.FindBehaviour<IButtonInputs<Keys>>();
|
||||||
|
buttonInputs?.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new RequestStartPacket()));
|
||||||
|
|
||||||
|
ball = Universe.FindRequiredBehaviour<Ball>();
|
||||||
|
networkClient = Universe.FindBehaviour<INetworkCommunicatorClient>();
|
||||||
|
networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
|
||||||
|
logger = Universe.FindBehaviour<ILogger>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ScoreToLeft()
|
||||||
|
{
|
||||||
|
ScoreLeft++;
|
||||||
|
OnScoreUpdated?.Invoke(this);
|
||||||
|
|
||||||
|
PostScoreUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ScoreToRight()
|
||||||
|
{
|
||||||
|
ScoreRight++;
|
||||||
|
OnScoreUpdated?.Invoke(this);
|
||||||
|
|
||||||
|
PostScoreUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
ScoreLeft = ScoreRight = 0;
|
||||||
|
|
||||||
|
PostScoreUpdate();
|
||||||
|
|
||||||
|
OnReset?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PostScoreUpdate()
|
||||||
|
{
|
||||||
|
ball.ResetBall();
|
||||||
|
|
||||||
|
if (networkServer is not null)
|
||||||
|
{
|
||||||
|
networkServer.SendToAll(new ScorePacket(this));
|
||||||
|
logger?.Log(this, $"Sending score update packet to all: {ScoreLeft} - {ScoreRight}");
|
||||||
|
}
|
||||||
|
|
||||||
|
int halfwayScore = (int)(WinScore * .5f);
|
||||||
|
if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore)
|
||||||
|
{
|
||||||
|
OnFinished?.Invoke(this);
|
||||||
|
logger?.Log(this, $"Game finished");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ball.LaunchBall(GetBallLaunchDirection());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2D GetBallLaunchDirection()
|
||||||
|
{
|
||||||
|
const float AllowedRadians = 45f * Syntriax.Engine.Core.Math.DegreeToRadian;
|
||||||
|
float rotation = (float)random.NextDouble() * 2f * AllowedRadians - AllowedRadians;
|
||||||
|
bool isBackwards = (random.Next() % 2) == 1;
|
||||||
|
return Vector2D.Right.Rotate(isBackwards ? rotation + Syntriax.Engine.Core.Math.Pi : rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPacketListenerClient<ScorePacket>.OnClientPacketArrived(IConnection sender, ScorePacket packet)
|
||||||
|
{
|
||||||
|
ScoreLeft = packet.Left;
|
||||||
|
ScoreRight = packet.Right;
|
||||||
|
|
||||||
|
OnScoreUpdated?.Invoke(this);
|
||||||
|
logger?.Log(this, $"Client score update packet arrived: {packet.Left} - {packet.Right}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPacketListenerServer<RequestStartPacket>.OnServerPacketArrived(IConnection sender, RequestStartPacket packet)
|
||||||
|
{
|
||||||
|
logger?.Log(this, $"{sender} requested start");
|
||||||
|
if (ball.RigidBody.Velocity.MagnitudeSquared > 0.01f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
ball.LaunchBall(GetBallLaunchDirection());
|
||||||
|
logger?.Log(this, $"Game started");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RequestStartPacket : INetworkPacket;
|
||||||
|
private class ScorePacket : INetworkPacket
|
||||||
|
{
|
||||||
|
public int Left { get; set; }
|
||||||
|
public int Right { get; set; }
|
||||||
|
|
||||||
|
public ScorePacket() { }
|
||||||
|
public ScorePacket(PongManager pongManagerBehaviour)
|
||||||
|
{
|
||||||
|
Left = pongManagerBehaviour.ScoreLeft;
|
||||||
|
Right = pongManagerBehaviour.ScoreRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
Shared/Behaviours/ScoreLabel.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class ScoreLabel(bool IsLeft) : Label, IFirstFrameUpdate
|
||||||
|
{
|
||||||
|
public readonly bool IsLeft = IsLeft;
|
||||||
|
|
||||||
|
private PongManager pongManager = null!;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
MonoGameWindow monoGameWindow = Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window;
|
||||||
|
Font = monoGameWindow.Content.Load<SpriteFont>("UbuntuMono");
|
||||||
|
|
||||||
|
pongManager = Universe.FindRequiredBehaviour<PongManager>();
|
||||||
|
|
||||||
|
pongManager.OnScoreUpdated += UpdateScores;
|
||||||
|
pongManager.OnReset += UpdateScores;
|
||||||
|
|
||||||
|
UpdateScores(pongManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScores(PongManager pongManager)
|
||||||
|
{
|
||||||
|
if (IsLeft)
|
||||||
|
Text = pongManager.ScoreLeft.ToString();
|
||||||
|
else
|
||||||
|
Text = pongManager.ScoreRight.ToString();
|
||||||
|
}
|
||||||
|
}
|
19
Shared/Behaviours/ScoreWall.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Physics2D;
|
||||||
|
|
||||||
|
namespace Pong.Behaviours;
|
||||||
|
|
||||||
|
public class ScoreWall(Action OnCollision) : Behaviour2D, IFirstFrameUpdate
|
||||||
|
{
|
||||||
|
private Action OnCollision { get; } = OnCollision;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
if (!BehaviourController.TryGetBehaviour(out ICollider2D? collider2D))
|
||||||
|
return;
|
||||||
|
|
||||||
|
collider2D.OnCollisionDetected.AddListener((_, _1) => OnCollision?.Invoke());
|
||||||
|
}
|
||||||
|
}
|
22
Shared/Content/Content.mgcb
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
#----------------------------- Global Properties ----------------------------#
|
||||||
|
|
||||||
|
/outputDir:bin/$(Platform)
|
||||||
|
/intermediateDir:obj/$(Platform)
|
||||||
|
/platform:DesktopGL
|
||||||
|
/config:
|
||||||
|
/profile:Reach
|
||||||
|
/compress:False
|
||||||
|
|
||||||
|
#-------------------------------- References --------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------- Content ---------------------------------#
|
||||||
|
|
||||||
|
#begin UbuntuMono.spritefont
|
||||||
|
/importer:FontDescriptionImporter
|
||||||
|
/processor:FontDescriptionProcessor
|
||||||
|
/processorParam:PremultiplyAlpha=True
|
||||||
|
/processorParam:TextureFormat=Compressed
|
||||||
|
/build:UbuntuMono.spritefont
|
||||||
|
|
BIN
Shared/Content/Ubuntu Mono.ttf
Normal file
60
Shared/Content/UbuntuMono.spritefont
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
This file contains an xml description of a font, and will be read by the XNA
|
||||||
|
Framework Content Pipeline. Follow the comments to customize the appearance
|
||||||
|
of the font in your game, and to change the characters which are available to draw
|
||||||
|
with.
|
||||||
|
-->
|
||||||
|
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
|
||||||
|
<Asset Type="Graphics:FontDescription">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Modify this string to change the font that will be imported.
|
||||||
|
-->
|
||||||
|
<FontName>Ubuntu Mono</FontName>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Size is a float value, measured in points. Modify this value to change
|
||||||
|
the size of the font.
|
||||||
|
-->
|
||||||
|
<Size>144</Size>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Spacing is a float value, measured in pixels. Modify this value to change
|
||||||
|
the amount of spacing in between characters.
|
||||||
|
-->
|
||||||
|
<Spacing>0</Spacing>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
UseKerning controls the layout of the font. If this value is true, kerning information
|
||||||
|
will be used when placing characters.
|
||||||
|
-->
|
||||||
|
<UseKerning>true</UseKerning>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
|
||||||
|
and "Bold, Italic", and are case sensitive.
|
||||||
|
-->
|
||||||
|
<Style>Bold</Style>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If you uncomment this line, the default character will be substituted if you draw
|
||||||
|
or measure text that contains characters which were not included in the font.
|
||||||
|
-->
|
||||||
|
<!-- <DefaultCharacter>*</DefaultCharacter> -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
CharacterRegions control what letters are available in the font. Every
|
||||||
|
character from Start to End will be built and made available for drawing. The
|
||||||
|
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
|
||||||
|
character set. The characters are ordered according to the Unicode standard.
|
||||||
|
See the documentation for more information.
|
||||||
|
-->
|
||||||
|
<CharacterRegions>
|
||||||
|
<CharacterRegion>
|
||||||
|
<Start> </Start>
|
||||||
|
<End>~</End>
|
||||||
|
</CharacterRegion>
|
||||||
|
</CharacterRegions>
|
||||||
|
</Asset>
|
||||||
|
</XnaContent>
|
12
Shared/Network/Abstract/IConnection.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface IConnection
|
||||||
|
{
|
||||||
|
string Id { get; }
|
||||||
|
|
||||||
|
float Ping { get; }
|
||||||
|
float RoundTrip { get; }
|
||||||
|
|
||||||
|
int PingMs { get; }
|
||||||
|
int RoundTripMs { get; }
|
||||||
|
}
|
6
Shared/Network/Abstract/IEntityNetworkPacket.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface IEntityNetworkPacket : INetworkPacket
|
||||||
|
{
|
||||||
|
string EntityId { get; }
|
||||||
|
}
|
37
Shared/Network/Abstract/INetworkCommunicator.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface INetworkCommunicator
|
||||||
|
{
|
||||||
|
Event<INetworkCommunicator, IConnection> OnConnectionEstablished { get; }
|
||||||
|
Event<INetworkCommunicator, IConnection> OnConnectionAbolished { get; }
|
||||||
|
|
||||||
|
IReadOnlyDictionary<string, IConnection> Connections { get; }
|
||||||
|
|
||||||
|
INetworkCommunicator Stop();
|
||||||
|
|
||||||
|
INetworkCommunicator SubscribeToPackets<T>(Event<IConnection, T>.EventHandler callback);
|
||||||
|
INetworkCommunicator UnsubscribeFromPackets<T>(Event<IConnection, T>.EventHandler callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface INetworkCommunicatorClient : INetworkCommunicator
|
||||||
|
{
|
||||||
|
INetworkCommunicatorClient Connect(string address, int port, string? password = null);
|
||||||
|
|
||||||
|
INetworkCommunicatorClient SendToServer<T>(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface INetworkCommunicatorServer : INetworkCommunicator
|
||||||
|
{
|
||||||
|
string Password { get; }
|
||||||
|
int MaxConnectionCount { get; }
|
||||||
|
int Port { get; }
|
||||||
|
|
||||||
|
INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null);
|
||||||
|
|
||||||
|
INetworkCommunicatorServer SendToClient<T>(IConnection connection, T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
|
||||||
|
INetworkCommunicatorServer SendToAll<T>(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
|
||||||
|
}
|
5
Shared/Network/Abstract/INetworkEntity.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface INetworkEntity : IEntity;
|
11
Shared/Network/Abstract/INetworkManager.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface INetworkManager
|
||||||
|
{
|
||||||
|
IReadOnlyDictionary<string, INetworkEntity> NetworkEntities { get; }
|
||||||
|
IBehaviourCollector<INetworkEntity> NetworkEntityCollector { get; }
|
||||||
|
}
|
3
Shared/Network/Abstract/INetworkPacket.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface INetworkPacket;
|
6
Shared/Network/Abstract/IPacketListenerClient.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface IPacketListenerClient<T> : INetworkEntity
|
||||||
|
{
|
||||||
|
void OnClientPacketArrived(IConnection sender, T packet);
|
||||||
|
}
|
6
Shared/Network/Abstract/IPacketListenerServer.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public interface IPacketListenerServer<T> : INetworkEntity
|
||||||
|
{
|
||||||
|
void OnServerPacketArrived(IConnection sender, T packet);
|
||||||
|
}
|
9
Shared/Network/Abstract/PacketDelivery.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public enum PacketDelivery
|
||||||
|
{
|
||||||
|
ReliableInOrder,
|
||||||
|
ReliableOutOfOrder,
|
||||||
|
UnreliableInOrder,
|
||||||
|
UnreliableOutOfOrder,
|
||||||
|
};
|
89
Shared/Network/LiteNetLib/LiteNetLibClient.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using LiteNetLib;
|
||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicatorClient
|
||||||
|
{
|
||||||
|
private readonly NetDataWriter netDataWriter = new();
|
||||||
|
|
||||||
|
private CancellationTokenSource? cancellationTokenSource = null;
|
||||||
|
|
||||||
|
protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibServerConnection(peer);
|
||||||
|
|
||||||
|
public INetworkCommunicatorClient Connect(string address, int port, string? password = null)
|
||||||
|
{
|
||||||
|
if (!UniverseObject.IsInUniverse)
|
||||||
|
throw new($"{nameof(LiteNetLibClient)} must be in an universe to connect");
|
||||||
|
|
||||||
|
password ??= string.Empty;
|
||||||
|
|
||||||
|
// Okay, for some reason sometimes LiteNetLib goes dumb when the server hostname has IPv6 address as well as IPv4
|
||||||
|
// but the client doesn't support IPv6, it still tries to use the v6 unless we explicitly tell the ip to connect to,
|
||||||
|
// which fails to connect... So for the time being I am preferring IPv4 below over IPv6 for clients.
|
||||||
|
// TODO: I think this is something that happens on Linux only? I need to check on Windows as well just to be sure.
|
||||||
|
System.Net.IPAddress[] addresses = System.Net.Dns.GetHostAddresses(address);
|
||||||
|
string connectionAddress = addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork, addresses[0]).ToString();
|
||||||
|
|
||||||
|
logger?.Log(this, $"Connecting to server at '{address}:{port}' with password '{password}'");
|
||||||
|
logger?.Log(this, $"Resolved address for {address}: {connectionAddress}");
|
||||||
|
|
||||||
|
Manager.Start();
|
||||||
|
Manager.Connect(connectionAddress, port, password);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INetworkCommunicatorClient SendToServer<T>(T packet, PacketDelivery packetDelivery) where T : class, new()
|
||||||
|
{
|
||||||
|
netDataWriter.Reset();
|
||||||
|
netPacketProcessor.Write(netDataWriter, packet);
|
||||||
|
|
||||||
|
switch (packetDelivery)
|
||||||
|
{
|
||||||
|
case PacketDelivery.ReliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
||||||
|
case PacketDelivery.UnreliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break;
|
||||||
|
case PacketDelivery.ReliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break;
|
||||||
|
case PacketDelivery.UnreliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break;
|
||||||
|
default: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
base.OnEnteredUniverse(universe);
|
||||||
|
|
||||||
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
PollEvents(cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
base.OnExitedUniverse(universe);
|
||||||
|
cancellationTokenSource?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client needs to send everything as soon as possible so
|
||||||
|
/// the events are polled a separate thread running constantly
|
||||||
|
/// </summary>
|
||||||
|
private async void PollEvents(CancellationToken cancellationToken) => await Task.Run(() =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Manager.PollEvents();
|
||||||
|
Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
16
Shared/Network/LiteNetLib/LiteNetLibClientConnection.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using LiteNetLib;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public record class LiteNetLibClientConnection(NetPeer NetPeer) : IConnection
|
||||||
|
{
|
||||||
|
public string Id { get; } = NetPeer.Id.ToString();
|
||||||
|
|
||||||
|
public float Ping => NetPeer.Ping * .001f;
|
||||||
|
public float RoundTrip => NetPeer.RoundTripTime * .001f;
|
||||||
|
|
||||||
|
public int PingMs => NetPeer.Ping;
|
||||||
|
public int RoundTripMs => NetPeer.RoundTripTime;
|
||||||
|
|
||||||
|
public override string ToString() => $"Connection({Id})";
|
||||||
|
}
|
189
Shared/Network/LiteNetLib/LiteNetLibCommunicatorBase.cs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using LiteNetLib;
|
||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicator
|
||||||
|
{
|
||||||
|
protected readonly NetPacketProcessor netPacketProcessor = new();
|
||||||
|
|
||||||
|
private readonly Dictionary<Type, Event<IConnection, object>> listeners = [];
|
||||||
|
private readonly Dictionary<string, IConnection> _connections = [];
|
||||||
|
|
||||||
|
private readonly Dictionary<int, IConnection> localPeerIdToConnectionDictionary = [];
|
||||||
|
|
||||||
|
protected ILogger? logger = null;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, IConnection> Connections => _connections;
|
||||||
|
public EventBasedNetListener Listener { get; private set; } = null!;
|
||||||
|
public NetManager Manager { get; private set; } = null!;
|
||||||
|
|
||||||
|
public Event<INetworkCommunicator, IConnection> OnConnectionEstablished { get; } = new();
|
||||||
|
public Event<INetworkCommunicator, IConnection> OnConnectionAbolished { get; } = new();
|
||||||
|
|
||||||
|
public INetworkCommunicator Stop()
|
||||||
|
{
|
||||||
|
Manager.Stop();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
base.OnEnteredUniverse(universe);
|
||||||
|
logger = universe.FindBehaviour<ILogger>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
base.OnExitedUniverse(universe);
|
||||||
|
logger = null;
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnPacketArrived<T>(T packet, NetPeer peer) where T : INetworkPacket
|
||||||
|
{
|
||||||
|
if (!listeners.TryGetValue(typeof(T), out Event<IConnection, object>? @event))
|
||||||
|
return;
|
||||||
|
|
||||||
|
@event.Invoke(localPeerIdToConnectionDictionary[peer.Id], packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
|
||||||
|
{
|
||||||
|
try { netPacketProcessor.ReadAllPackets(reader, peer); }
|
||||||
|
catch (Exception exception) { logger?.LogException(this, exception, force: true); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract IConnection GetConnection(NetPeer peer);
|
||||||
|
private void ConnectionEstablished(NetPeer peer)
|
||||||
|
{
|
||||||
|
IConnection connection = GetConnection(peer);
|
||||||
|
|
||||||
|
localPeerIdToConnectionDictionary.Add(peer.Id, connection);
|
||||||
|
_connections.Add(connection.Id, connection);
|
||||||
|
|
||||||
|
logger?.Log(this, $"Connection established with ip '{peer.Address}' and id '{connection.Id}'");
|
||||||
|
OnConnectionEstablished.Invoke(this, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ConnectionAbolished(NetPeer peer, DisconnectInfo disconnectInfo)
|
||||||
|
{
|
||||||
|
if (!localPeerIdToConnectionDictionary.TryGetValue(peer.Id, out var connection))
|
||||||
|
return;
|
||||||
|
|
||||||
|
localPeerIdToConnectionDictionary.Remove(peer.Id);
|
||||||
|
_connections.Remove(connection.Id);
|
||||||
|
|
||||||
|
logger?.Log(this, $"Connection abolished with ip '{peer.Address}' and id '{connection.Id}'");
|
||||||
|
OnConnectionAbolished.Invoke(this, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupPackets()
|
||||||
|
{
|
||||||
|
// Find network packets implementing INetworkPacket
|
||||||
|
IEnumerable<Type> packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
|
||||||
|
.Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType);
|
||||||
|
|
||||||
|
MethodInfo subscribeReusableMethod = typeof(NetPacketProcessor)
|
||||||
|
.GetMethods()
|
||||||
|
.FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) &&
|
||||||
|
m.GetParameters().Length == 1 &&
|
||||||
|
m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>)
|
||||||
|
)!;
|
||||||
|
|
||||||
|
MethodInfo registerNestedTypeMethod = typeof(NetPacketProcessor)
|
||||||
|
.GetMethods()
|
||||||
|
.FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType) &&
|
||||||
|
m.GetParameters().Length == 0
|
||||||
|
)!;
|
||||||
|
|
||||||
|
MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase)
|
||||||
|
.GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
|
||||||
|
// Register all network packets by calling the methods bellow where T is our found network packet type
|
||||||
|
// NetPacketProcessor.SubscribeReusable<T, NetPeer>(Action<T, NetPeer> onReceive)
|
||||||
|
// NetPacketProcessor.RegisterNestedType<T>()
|
||||||
|
foreach (Type packetType in packetTypes)
|
||||||
|
{
|
||||||
|
if (!packetType.IsClass)
|
||||||
|
{
|
||||||
|
MethodInfo genericRegisterNestedTypeMethod = registerNestedTypeMethod.MakeGenericMethod(packetType);
|
||||||
|
genericRegisterNestedTypeMethod.Invoke(netPacketProcessor, []);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType);
|
||||||
|
|
||||||
|
Type delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer));
|
||||||
|
Delegate delegateHandler = Delegate.CreateDelegate(delegateType, this, genericOnPacketArrivedMethod);
|
||||||
|
|
||||||
|
var genericSubscribeReusableMethod = subscribeReusableMethod.MakeGenericMethod(packetType, typeof(NetPeer));
|
||||||
|
genericSubscribeReusableMethod.Invoke(netPacketProcessor, [delegateHandler]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteNetLibCommunicatorBase()
|
||||||
|
{
|
||||||
|
Listener = new EventBasedNetListener();
|
||||||
|
Manager = new NetManager(Listener);
|
||||||
|
|
||||||
|
Listener.NetworkReceiveEvent += NetworkReceiveEvent;
|
||||||
|
Listener.PeerConnectedEvent += ConnectionEstablished;
|
||||||
|
Listener.PeerDisconnectedEvent += ConnectionAbolished;
|
||||||
|
|
||||||
|
SetupEnginePackets();
|
||||||
|
SetupPackets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupEnginePackets()
|
||||||
|
{
|
||||||
|
// I know, ugly af. I need to find a better way
|
||||||
|
netPacketProcessor.RegisterNestedType(AABBNetPacker.Write, AABBNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read);
|
||||||
|
netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INetworkCommunicator SubscribeToPackets<T>(Event<IConnection, T>.EventHandler callback)
|
||||||
|
{
|
||||||
|
Type type = typeof(T);
|
||||||
|
if (!listeners.TryGetValue(type, out Event<IConnection, object>? @event))
|
||||||
|
{
|
||||||
|
@event = new();
|
||||||
|
listeners.Add(type, @event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@event.AddListener(EventDelegateWrapper(callback));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INetworkCommunicator UnsubscribeFromPackets<T>(Event<IConnection, T>.EventHandler callback)
|
||||||
|
{
|
||||||
|
Type type = typeof(T);
|
||||||
|
if (!listeners.TryGetValue(type, out Event<IConnection, object>? @event))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
@event.RemoveListener(EventDelegateWrapper(callback));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Event<IConnection, object>.EventHandler EventDelegateWrapper<T>(Event<IConnection, T>.EventHandler callback) => (sender, @object) => callback.Invoke(sender, (T)@object);
|
||||||
|
}
|
104
Shared/Network/LiteNetLib/LiteNetLibServer.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using LiteNetLib;
|
||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicatorServer
|
||||||
|
{
|
||||||
|
public string Password { get; private set; } = string.Empty;
|
||||||
|
public int MaxConnectionCount { get; private set; } = 2;
|
||||||
|
public int Port { get; private set; } = 8888;
|
||||||
|
|
||||||
|
private readonly NetDataWriter netDataWriter = new();
|
||||||
|
|
||||||
|
public LiteNetLibServer() : this(8888, 2) { }
|
||||||
|
public LiteNetLibServer(int port, int maxConnectionCount) : base()
|
||||||
|
{
|
||||||
|
MaxConnectionCount = maxConnectionCount;
|
||||||
|
Port = port;
|
||||||
|
|
||||||
|
Listener.ConnectionRequestEvent += OnConnectionRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibClientConnection(peer);
|
||||||
|
|
||||||
|
private void OnConnectionRequest(ConnectionRequest request)
|
||||||
|
{
|
||||||
|
logger?.Log(this, $"Connection request from ip {request.RemoteEndPoint}");
|
||||||
|
logger?.Log(this, $"Current connection count: {Connections.Count}");
|
||||||
|
if (Manager.ConnectedPeersCount < MaxConnectionCount)
|
||||||
|
request.AcceptIfKey(Password);
|
||||||
|
else
|
||||||
|
request.Reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null)
|
||||||
|
{
|
||||||
|
if (!UniverseObject.IsInUniverse)
|
||||||
|
throw new($"{nameof(LiteNetLibServer)} must be in an universe to start");
|
||||||
|
|
||||||
|
Password = password ?? string.Empty;
|
||||||
|
MaxConnectionCount = maxConnectionCount;
|
||||||
|
Port = port;
|
||||||
|
|
||||||
|
logger?.Log(this, $"Starting server on port '{port}' with password '{Password}' and max connection count '{maxConnectionCount}'");
|
||||||
|
logger?.Log(this, $"Server status: {(Manager.Start(port) ? "Active" : "Failed")}");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INetworkCommunicatorServer SendToClient<T>(IConnection connection, T packet, PacketDelivery packetDelivery) where T : class, new()
|
||||||
|
{
|
||||||
|
netDataWriter.Reset();
|
||||||
|
netPacketProcessor.Write(netDataWriter, packet);
|
||||||
|
|
||||||
|
if (Manager.ConnectedPeerList.FirstOrDefault(p => p.Id.CompareTo(connection.Id) == 0) is not NetPeer netPeer)
|
||||||
|
throw new($"Peer {connection} couldn't be found.");
|
||||||
|
|
||||||
|
switch (packetDelivery)
|
||||||
|
{
|
||||||
|
case PacketDelivery.ReliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
||||||
|
case PacketDelivery.UnreliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break;
|
||||||
|
case PacketDelivery.ReliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break;
|
||||||
|
case PacketDelivery.UnreliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break;
|
||||||
|
default: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INetworkCommunicatorServer SendToAll<T>(T packet, PacketDelivery packetDelivery) where T : class, new()
|
||||||
|
{
|
||||||
|
netDataWriter.Reset();
|
||||||
|
netPacketProcessor.Write(netDataWriter, packet);
|
||||||
|
|
||||||
|
switch (packetDelivery)
|
||||||
|
{
|
||||||
|
case PacketDelivery.ReliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
||||||
|
case PacketDelivery.UnreliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Sequenced); break;
|
||||||
|
case PacketDelivery.ReliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableUnordered); break;
|
||||||
|
case PacketDelivery.UnreliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Unreliable); break;
|
||||||
|
default: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PollEvents(IUniverse sender, IUniverse.UpdateArguments args) => Manager.PollEvents();
|
||||||
|
|
||||||
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
base.OnEnteredUniverse(universe);
|
||||||
|
universe.OnPostUpdate.AddListener(PollEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
base.OnExitedUniverse(universe);
|
||||||
|
universe.OnPostUpdate.RemoveListener(PollEvents);
|
||||||
|
}
|
||||||
|
}
|
16
Shared/Network/LiteNetLib/LiteNetLibServerConnection.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using LiteNetLib;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
public record class LiteNetLibServerConnection(NetPeer NetPeer) : IConnection
|
||||||
|
{
|
||||||
|
public string Id => NetPeer.RemoteId.ToString();
|
||||||
|
|
||||||
|
public float Ping => NetPeer.Ping * .001f;
|
||||||
|
public float RoundTrip => NetPeer.RoundTripTime * .001f;
|
||||||
|
|
||||||
|
public int PingMs => NetPeer.Ping;
|
||||||
|
public int RoundTripMs => NetPeer.RoundTripTime;
|
||||||
|
|
||||||
|
public override string ToString() => $"Connection({Id})";
|
||||||
|
}
|
22
Shared/Network/LiteNetLib/Packers/AABBNetPacker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class AABBNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, AABB data)
|
||||||
|
{
|
||||||
|
Vector2DNetPacker.Write(writer, data.LowerBoundary);
|
||||||
|
Vector2DNetPacker.Write(writer, data.UpperBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static AABB Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
Vector2D lowerBoundary = Vector2DNetPacker.Read(reader);
|
||||||
|
Vector2D upperBoundary = Vector2DNetPacker.Read(reader);
|
||||||
|
|
||||||
|
return new AABB(lowerBoundary, upperBoundary);
|
||||||
|
}
|
||||||
|
}
|
22
Shared/Network/LiteNetLib/Packers/CircleNetPacker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class CircleNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Circle data)
|
||||||
|
{
|
||||||
|
Vector2DNetPacker.Write(writer, data.Center);
|
||||||
|
writer.Put(data.Radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Circle Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
Vector2D center = Vector2DNetPacker.Read(reader);
|
||||||
|
float radius = reader.GetFloat();
|
||||||
|
|
||||||
|
return new Circle(center, radius);
|
||||||
|
}
|
||||||
|
}
|
24
Shared/Network/LiteNetLib/Packers/ColorHSVNetPacker.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class ColorHSVNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, ColorHSV data)
|
||||||
|
{
|
||||||
|
writer.Put(data.Hue);
|
||||||
|
writer.Put(data.Saturation);
|
||||||
|
writer.Put(data.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ColorHSV Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
float hue = reader.GetFloat();
|
||||||
|
float saturation = reader.GetFloat();
|
||||||
|
float value = reader.GetFloat();
|
||||||
|
|
||||||
|
return new ColorHSV(hue, saturation, value);
|
||||||
|
}
|
||||||
|
}
|
26
Shared/Network/LiteNetLib/Packers/ColorRGBANetPacker.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class ColorRGBANetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, ColorRGBA data)
|
||||||
|
{
|
||||||
|
writer.Put(data.R);
|
||||||
|
writer.Put(data.G);
|
||||||
|
writer.Put(data.B);
|
||||||
|
writer.Put(data.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ColorRGBA Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
byte red = reader.GetByte();
|
||||||
|
byte green = reader.GetByte();
|
||||||
|
byte blue = reader.GetByte();
|
||||||
|
byte alpha = reader.GetByte();
|
||||||
|
|
||||||
|
return new ColorRGBA(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
}
|
24
Shared/Network/LiteNetLib/Packers/ColorRGBNetPacker.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class ColorRGBNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, ColorRGB data)
|
||||||
|
{
|
||||||
|
writer.Put(data.R);
|
||||||
|
writer.Put(data.G);
|
||||||
|
writer.Put(data.B);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ColorRGB Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
byte red = reader.GetByte();
|
||||||
|
byte green = reader.GetByte();
|
||||||
|
byte blue = reader.GetByte();
|
||||||
|
|
||||||
|
return new ColorRGB(red, green, blue);
|
||||||
|
}
|
||||||
|
}
|
22
Shared/Network/LiteNetLib/Packers/Line2DEquationNetPacker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class Line2DEquationNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Line2DEquation data)
|
||||||
|
{
|
||||||
|
writer.Put(data.Slope);
|
||||||
|
writer.Put(data.OffsetY);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Line2DEquation Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
float slope = reader.GetFloat();
|
||||||
|
float offsetY = reader.GetFloat();
|
||||||
|
|
||||||
|
return new Line2DEquation(slope, offsetY);
|
||||||
|
}
|
||||||
|
}
|
22
Shared/Network/LiteNetLib/Packers/Line2DNetPacker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class Line2DNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Line2D data)
|
||||||
|
{
|
||||||
|
Vector2DNetPacker.Write(writer, data.From);
|
||||||
|
Vector2DNetPacker.Write(writer, data.To);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Line2D Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
Vector2D from = Vector2DNetPacker.Read(reader);
|
||||||
|
Vector2D to = Vector2DNetPacker.Read(reader);
|
||||||
|
|
||||||
|
return new Line2D(from, to);
|
||||||
|
}
|
||||||
|
}
|
22
Shared/Network/LiteNetLib/Packers/Projection1DNetPacker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class Projection1DNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Projection1D data)
|
||||||
|
{
|
||||||
|
writer.Put(data.Min);
|
||||||
|
writer.Put(data.Max);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Projection1D Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
float min = reader.GetFloat();
|
||||||
|
float max = reader.GetFloat();
|
||||||
|
|
||||||
|
return new Projection1D(min, max);
|
||||||
|
}
|
||||||
|
}
|
26
Shared/Network/LiteNetLib/Packers/QuaternionNetPacker.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class QuaternionNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Quaternion data)
|
||||||
|
{
|
||||||
|
writer.Put(data.X);
|
||||||
|
writer.Put(data.Y);
|
||||||
|
writer.Put(data.Z);
|
||||||
|
writer.Put(data.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Quaternion Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
float x = reader.GetFloat();
|
||||||
|
float y = reader.GetFloat();
|
||||||
|
float z = reader.GetFloat();
|
||||||
|
float w = reader.GetFloat();
|
||||||
|
|
||||||
|
return new Quaternion(x, y, z, w);
|
||||||
|
}
|
||||||
|
}
|
28
Shared/Network/LiteNetLib/Packers/Shape2DNetPacker.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class Shape2DNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Shape2D data)
|
||||||
|
{
|
||||||
|
writer.Put(data.Vertices.Count);
|
||||||
|
foreach (Vector2D vertex in data.Vertices)
|
||||||
|
Vector2DNetPacker.Write(writer, vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Shape2D Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
int verticesCount = reader.GetInt();
|
||||||
|
List<Vector2D> vertices = new(verticesCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < verticesCount; i++)
|
||||||
|
vertices.Add(Vector2DNetPacker.Read(reader));
|
||||||
|
|
||||||
|
return new Shape2D(vertices);
|
||||||
|
}
|
||||||
|
}
|
24
Shared/Network/LiteNetLib/Packers/TriangleNetPacker.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class TriangleNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Triangle data)
|
||||||
|
{
|
||||||
|
Vector2DNetPacker.Write(writer, data.A);
|
||||||
|
Vector2DNetPacker.Write(writer, data.B);
|
||||||
|
Vector2DNetPacker.Write(writer, data.C);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Triangle Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
Vector2D a = Vector2DNetPacker.Read(reader);
|
||||||
|
Vector2D b = Vector2DNetPacker.Read(reader);
|
||||||
|
Vector2D c = Vector2DNetPacker.Read(reader);
|
||||||
|
|
||||||
|
return new Triangle(a, b, c);
|
||||||
|
}
|
||||||
|
}
|
22
Shared/Network/LiteNetLib/Packers/Vector2DNetPacker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class Vector2DNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Vector2D data)
|
||||||
|
{
|
||||||
|
writer.Put(data.X);
|
||||||
|
writer.Put(data.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Vector2D Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
float x = reader.GetFloat();
|
||||||
|
float y = reader.GetFloat();
|
||||||
|
|
||||||
|
return new Vector2D(x, y);
|
||||||
|
}
|
||||||
|
}
|
24
Shared/Network/LiteNetLib/Packers/Vector3DNetPacker.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using LiteNetLib.Utils;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
internal static class Vector3DNetPacker
|
||||||
|
{
|
||||||
|
internal static void Write(NetDataWriter writer, Vector3D data)
|
||||||
|
{
|
||||||
|
writer.Put(data.X);
|
||||||
|
writer.Put(data.Y);
|
||||||
|
writer.Put(data.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Vector3D Read(NetDataReader reader)
|
||||||
|
{
|
||||||
|
float x = reader.GetFloat();
|
||||||
|
float y = reader.GetFloat();
|
||||||
|
float z = reader.GetFloat();
|
||||||
|
|
||||||
|
return new Vector3D(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
333
Shared/Network/NetworkManager.cs
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Network;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Intermediary manager that looks up in it's hierarchy for a <see cref="INetworkCommunicator"/> to route/broadcast it's received packets to their destinations.
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkManager : Behaviour, INetworkManager
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, Dictionary<Type, List<MethodInfo>>> clientPacketArrivalMethods = [];
|
||||||
|
private readonly Dictionary<Type, Dictionary<Type, List<MethodInfo>>> serverPacketArrivalMethods = [];
|
||||||
|
|
||||||
|
private readonly Dictionary<Type, Dictionary<string, object>> clientPacketRouters = [];
|
||||||
|
private readonly Dictionary<Type, Dictionary<string, object>> serverPacketRouters = [];
|
||||||
|
|
||||||
|
private readonly List<(Type packetType, Delegate @delegate)> packetRetrievalDelegates = [];
|
||||||
|
|
||||||
|
private readonly Dictionary<Type, MethodInfo> clearRoutesMethods = [];
|
||||||
|
private readonly Dictionary<Type, MethodInfo> registerPacketListenersMethods = [];
|
||||||
|
|
||||||
|
private readonly Dictionary<string, INetworkEntity> _networkEntities = [];
|
||||||
|
public IReadOnlyDictionary<string, INetworkEntity> NetworkEntities => _networkEntities;
|
||||||
|
|
||||||
|
private readonly BehaviourCollector<INetworkEntity> _networkEntityCollector = new();
|
||||||
|
public IBehaviourCollector<INetworkEntity> NetworkEntityCollector => _networkEntityCollector;
|
||||||
|
|
||||||
|
private INetworkCommunicator _networkCommunicator = null!;
|
||||||
|
public INetworkCommunicator NetworkCommunicator
|
||||||
|
{
|
||||||
|
get => _networkCommunicator;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_networkCommunicator == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var previousCommunicator = _networkCommunicator;
|
||||||
|
_networkCommunicator = value;
|
||||||
|
|
||||||
|
if (previousCommunicator is not null) UnsubscribeCommunicatorMethods(previousCommunicator);
|
||||||
|
if (_networkCommunicator is not null) SubscribeCommunicatorMethods(_networkCommunicator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Communicator Subscriptions
|
||||||
|
private void SubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator)
|
||||||
|
{
|
||||||
|
MethodInfo subscribeToPacketsMethod = typeof(INetworkCommunicator)
|
||||||
|
.GetMethod(nameof(INetworkCommunicator.SubscribeToPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!;
|
||||||
|
|
||||||
|
foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates)
|
||||||
|
{
|
||||||
|
MethodInfo genericSubscribeMethod = subscribeToPacketsMethod.MakeGenericMethod(packetType);
|
||||||
|
genericSubscribeMethod.Invoke(networkCommunicator, [@delegate]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator)
|
||||||
|
{
|
||||||
|
MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator)
|
||||||
|
.GetMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!;
|
||||||
|
|
||||||
|
foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates)
|
||||||
|
{
|
||||||
|
MethodInfo genericUnsubscribeMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType);
|
||||||
|
genericUnsubscribeMethod.Invoke(networkCommunicator, [@delegate]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Packet Routing
|
||||||
|
private void OnPacketReceived<T>(IConnection sender, T entityDataPacket)
|
||||||
|
{
|
||||||
|
if (entityDataPacket is IEntityNetworkPacket entityPacket)
|
||||||
|
RoutePacket(sender, entityDataPacket, entityPacket);
|
||||||
|
else
|
||||||
|
BroadcastPacket(sender, entityDataPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RoutePacket<T>(IConnection sender, T entityDataPacket, IEntityNetworkPacket entityPacket)
|
||||||
|
{
|
||||||
|
if (NetworkCommunicator is INetworkCommunicatorClient)
|
||||||
|
RoutePacket(clientPacketRouters, entityPacket.EntityId, sender, entityDataPacket);
|
||||||
|
if (NetworkCommunicator is INetworkCommunicatorServer)
|
||||||
|
RoutePacket(serverPacketRouters, entityPacket.EntityId, sender, entityDataPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BroadcastPacket<T>(IConnection sender, T entityDataPacket)
|
||||||
|
{
|
||||||
|
if (NetworkCommunicator is INetworkCommunicatorClient)
|
||||||
|
BroadcastPacket(clientPacketRouters, sender, entityDataPacket);
|
||||||
|
if (NetworkCommunicator is INetworkCommunicatorServer)
|
||||||
|
BroadcastPacket(serverPacketRouters, sender, entityDataPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void BroadcastPacket<T>(
|
||||||
|
Dictionary<Type, Dictionary<string, object>> packetRouters,
|
||||||
|
IConnection sender,
|
||||||
|
T entityDataPacket)
|
||||||
|
{
|
||||||
|
if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary<string, object>? routers))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach ((string behaviourId, object routerEventReference) in routers)
|
||||||
|
{
|
||||||
|
Event<IConnection, T> routerEvent = (Event<IConnection, T>)routerEventReference;
|
||||||
|
routerEvent.Invoke(sender, entityDataPacket!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RoutePacket<T>(
|
||||||
|
Dictionary<Type, Dictionary<string, object>> packetRouters,
|
||||||
|
string entityId,
|
||||||
|
IConnection sender,
|
||||||
|
T entityDataPacket)
|
||||||
|
{
|
||||||
|
if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary<string, object>? routers))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!routers.TryGetValue(entityId, out object? routerEventReference))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Event<IConnection, T> routerEvent = (Event<IConnection, T>)routerEventReference;
|
||||||
|
routerEvent.Invoke(sender, entityDataPacket!);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Packet Routers
|
||||||
|
private void RegisterPacketRoutersFor(
|
||||||
|
INetworkEntity behaviour,
|
||||||
|
Dictionary<Type, Dictionary<string, object>> packetRouters,
|
||||||
|
Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods,
|
||||||
|
NetworkType networkType)
|
||||||
|
{
|
||||||
|
if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary<Type, List<MethodInfo>>? arrivalMethods))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach ((Type packetType, List<MethodInfo> methods) in arrivalMethods)
|
||||||
|
foreach (MethodInfo receiveMethod in methods)
|
||||||
|
{
|
||||||
|
if (!packetRouters.TryGetValue(packetType, out Dictionary<string, object>? routers))
|
||||||
|
{
|
||||||
|
routers = [];
|
||||||
|
packetRouters.Add(packetType, routers);
|
||||||
|
}
|
||||||
|
|
||||||
|
object packetListenerEvent = CreateEventAndRegister(packetType, behaviour, networkType);
|
||||||
|
routers.Add(behaviour.Id, packetListenerEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object CreateEventAndRegister(Type packetType, INetworkEntity behaviour, NetworkType networkType)
|
||||||
|
{
|
||||||
|
Type genericEventType = typeof(Event<,>).MakeGenericType(typeof(IConnection), packetType);
|
||||||
|
object packetListenerEvent = Activator.CreateInstance(genericEventType)!;
|
||||||
|
|
||||||
|
if (!registerPacketListenersMethods.TryGetValue(packetType, out MethodInfo? registerPacketListenerMethod))
|
||||||
|
throw new($"{nameof(RegisterPacketListenerEvent)} for {packetType.Name} has not been cached.");
|
||||||
|
|
||||||
|
registerPacketListenerMethod.Invoke(this, [behaviour, packetListenerEvent, networkType]);
|
||||||
|
return packetListenerEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterPacketListenerEvent<T>(
|
||||||
|
INetworkEntity behaviour,
|
||||||
|
Event<IConnection, T> packetListenerEvent,
|
||||||
|
NetworkType networkType)
|
||||||
|
{
|
||||||
|
switch (networkType)
|
||||||
|
{
|
||||||
|
case NetworkType.Client: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerClient<T>)behaviour).OnClientPacketArrived(sender, packet)); break;
|
||||||
|
case NetworkType.Server: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerServer<T>)behaviour).OnServerPacketArrived(sender, packet)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterPacketRoutersFor(
|
||||||
|
INetworkEntity behaviour,
|
||||||
|
Dictionary<Type, Dictionary<string, object>> packetRouters,
|
||||||
|
Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods)
|
||||||
|
{
|
||||||
|
if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary<Type, List<MethodInfo>>? arrivalMethods))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach ((Type packetType, List<MethodInfo> methods) in arrivalMethods)
|
||||||
|
{
|
||||||
|
if (!packetRouters.TryGetValue(packetType, out Dictionary<string, object>? routers))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!routers.TryGetValue(behaviour.Id, out object? routerEventReference))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!clearRoutesMethods.TryGetValue(packetType, out MethodInfo? clearRouterMethod))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
clearRouterMethod.Invoke(this, [routerEventReference]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearRouter<T>(object routerEventReference)
|
||||||
|
{
|
||||||
|
Event<IConnection, T> routerEvent = (Event<IConnection, T>)routerEventReference;
|
||||||
|
routerEvent.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Engine Callbacks
|
||||||
|
private void OnCollected(IBehaviourCollector<INetworkEntity> sender, IBehaviourCollector<INetworkEntity>.BehaviourCollectedArguments args)
|
||||||
|
{
|
||||||
|
INetworkEntity collectedBehaviour = args.BehaviourCollected;
|
||||||
|
|
||||||
|
if (!_networkEntities.TryAdd(collectedBehaviour.Id, collectedBehaviour))
|
||||||
|
throw new($"Unable to add {collectedBehaviour.Id} to {nameof(NetworkManager)}");
|
||||||
|
|
||||||
|
RegisterPacketRoutersFor(collectedBehaviour, clientPacketRouters, clientPacketArrivalMethods, NetworkType.Client);
|
||||||
|
RegisterPacketRoutersFor(collectedBehaviour, serverPacketRouters, serverPacketArrivalMethods, NetworkType.Server);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemoved(IBehaviourCollector<INetworkEntity> sender, IBehaviourCollector<INetworkEntity>.BehaviourRemovedArguments args)
|
||||||
|
{
|
||||||
|
INetworkEntity removedBehaviour = args.BehaviourRemoved;
|
||||||
|
if (!_networkEntities.Remove(args.BehaviourRemoved.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
UnregisterPacketRoutersFor(removedBehaviour, clientPacketRouters, clientPacketArrivalMethods);
|
||||||
|
UnregisterPacketRoutersFor(removedBehaviour, serverPacketRouters, serverPacketArrivalMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe) => _networkEntityCollector.Unassign();
|
||||||
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
_networkEntityCollector.Assign(universe);
|
||||||
|
NetworkCommunicator = BehaviourController.GetRequiredBehaviourInParent<INetworkCommunicator>();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
public NetworkManager()
|
||||||
|
{
|
||||||
|
CachePacketRetrievalDelegates();
|
||||||
|
CacheRegistrationMethods();
|
||||||
|
CachePacketArrivalMethods();
|
||||||
|
|
||||||
|
_networkEntityCollector.OnCollected.AddListener(OnCollected);
|
||||||
|
_networkEntityCollector.OnRemoved.AddListener(OnRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CachePacketRetrievalDelegates()
|
||||||
|
{
|
||||||
|
IEnumerable<Type> packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
|
||||||
|
.Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType);
|
||||||
|
|
||||||
|
MethodInfo onPacketArrivedMethod = GetType()
|
||||||
|
.GetMethod(nameof(OnPacketReceived), BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
|
||||||
|
foreach (Type packetType in packetTypes)
|
||||||
|
{
|
||||||
|
MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType);
|
||||||
|
|
||||||
|
Type genericDelegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(IConnection), packetType);
|
||||||
|
Delegate genericPacketReceivedDelegate = Delegate.CreateDelegate(genericDelegateType, this, genericOnPacketArrivedMethod);
|
||||||
|
|
||||||
|
packetRetrievalDelegates.Add((packetType, genericPacketReceivedDelegate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheRegistrationMethods()
|
||||||
|
{
|
||||||
|
CacheRegistrationMethods(registerPacketListenersMethods, nameof(RegisterPacketListenerEvent));
|
||||||
|
CacheRegistrationMethods(clearRoutesMethods, nameof(ClearRouter));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheRegistrationMethods(Dictionary<Type, MethodInfo> registrationMethods, string methodName)
|
||||||
|
{
|
||||||
|
MethodInfo registerPacketMethod = typeof(NetworkManager).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates)
|
||||||
|
{
|
||||||
|
MethodInfo genericMethod = registerPacketMethod.MakeGenericMethod(packetType);
|
||||||
|
registrationMethods.Add(packetType, genericMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CachePacketArrivalMethods()
|
||||||
|
{
|
||||||
|
CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<INetworkEntity>.OnClientPacketArrived));
|
||||||
|
CachePacketArrivalMethods(serverPacketArrivalMethods, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer<INetworkEntity>.OnServerPacketArrived));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CachePacketArrivalMethods(Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods, Type listenerType, string packetArrivalMethodName)
|
||||||
|
{
|
||||||
|
foreach (Type listenerClass in GetGenericsWith(listenerType))
|
||||||
|
{
|
||||||
|
Dictionary<Type, List<MethodInfo>> packetRouters = [];
|
||||||
|
packetArrivalMethods.Add(listenerClass, packetRouters);
|
||||||
|
|
||||||
|
foreach (Type packetListener in GetGenericInterfacesWith(listenerType, listenerClass))
|
||||||
|
{
|
||||||
|
Type packetType = packetListener.GetGenericArguments().First();
|
||||||
|
|
||||||
|
List<MethodInfo> arrivalMethods = packetListener
|
||||||
|
.GetMethods()
|
||||||
|
.Where(m => m.Name == packetArrivalMethodName)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
packetRouters.Add(packetType, arrivalMethods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Type> GetGenericsWith(Type type)
|
||||||
|
=> AppDomain.CurrentDomain
|
||||||
|
.GetAssemblies()
|
||||||
|
.SelectMany(a =>
|
||||||
|
a.GetTypes().Where(
|
||||||
|
t => t.GetInterfaces().Any(
|
||||||
|
i => i.IsGenericType && i.GetGenericTypeDefinition() == type
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static IEnumerable<Type> GetGenericInterfacesWith(Type interfaceType, Type type)
|
||||||
|
=> type.GetInterfaces().Where(
|
||||||
|
i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType
|
||||||
|
);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private enum NetworkType { Client, Server }
|
||||||
|
}
|
123
Shared/PongUniverse.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
|
||||||
|
using Pong.Behaviours;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Factory;
|
||||||
|
using Syntriax.Engine.Integration.MonoGame;
|
||||||
|
using Syntriax.Engine.Network;
|
||||||
|
using Syntriax.Engine.Physics2D;
|
||||||
|
using Syntriax.Engine.Systems.Tween;
|
||||||
|
|
||||||
|
namespace Pong;
|
||||||
|
|
||||||
|
public static class PongUniverse
|
||||||
|
{
|
||||||
|
public static IUniverse ApplyPongClient(Universe universe, string server, int port)
|
||||||
|
{
|
||||||
|
LiteNetLibClient client = universe.InstantiateUniverseObject().SetUniverseObject("Client").BehaviourController.AddBehaviour<LiteNetLibClient>();
|
||||||
|
client.BehaviourController.AddBehaviour<NetworkManager>();
|
||||||
|
universe.OnPreUpdate.AddOneTimeListener((_, _) => client.Connect(server, port));
|
||||||
|
|
||||||
|
DrawManager drawManager = universe.InstantiateUniverseObject().SetUniverseObject("Draw Manager").BehaviourController.AddBehaviour<DrawManager>();
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Triangle Batcher", drawManager.UniverseObject).BehaviourController.AddBehaviour<TriangleBatcher>();
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Sprite Batcher", drawManager.UniverseObject).BehaviourController.AddBehaviour<SpriteBatcher>();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Camera")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>()
|
||||||
|
.BehaviourController.AddBehaviour<CameraController>()
|
||||||
|
.BehaviourController.AddBehaviour<MonoGameCamera2D>();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Score Left")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(-250f, 250f), scale: Vector2D.One * .25f)
|
||||||
|
.BehaviourController.AddBehaviour<ScoreLabel>(true);
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Score Right")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(250f, 250f), scale: Vector2D.One * .25f)
|
||||||
|
.BehaviourController.AddBehaviour<ScoreLabel>(false);
|
||||||
|
|
||||||
|
return universe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniverse ApplyPongServer(Universe universe, int port)
|
||||||
|
{
|
||||||
|
LiteNetLibServer server = universe.InstantiateUniverseObject().SetUniverseObject("Server").BehaviourController.AddBehaviour<LiteNetLibServer>();
|
||||||
|
server.BehaviourController.AddBehaviour<NetworkManager>();
|
||||||
|
universe.OnPreUpdate.AddOneTimeListener((_, _) => server.Start(port, 2));
|
||||||
|
|
||||||
|
return universe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniverse ApplyPongUniverse(Universe universe)
|
||||||
|
{
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Update Manager").BehaviourController.AddBehaviour<UpdateManager>();
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Coroutine Manager").BehaviourController.AddBehaviour<CoroutineManager>();
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Tween Manager").BehaviourController.AddBehaviour<TweenManager>();
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Physics Engine 2D").BehaviourController.AddBehaviour<PhysicsEngine2D>();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
PongManager pongManager = universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Manager")
|
||||||
|
.BehaviourController.AddBehaviour<PongManager>(5);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Ball")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(0, 0f), scale: new Vector2D(10f, 10f))
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderCircle>(new Circle(Vector2D.Zero, 1f))
|
||||||
|
.BehaviourController.AddBehaviour<Ball>()
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
IUniverseObject leftPaddle = UniverseObjectFactory.Instantiate().SetUniverseObject("Left Paddle");
|
||||||
|
leftPaddle.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(-468f, 0f), scale: new Vector2D(15f, 60f))
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderShape>(Shape2D.Square)
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||||
|
Paddle leftPaddleBehaviour = BehaviourFactory.Instantiate<Paddle>(Keys.W, Keys.S, 228f, -228f, 400f);
|
||||||
|
leftPaddleBehaviour.Id = "lp";
|
||||||
|
leftPaddle.BehaviourController.AddBehaviour(leftPaddleBehaviour);
|
||||||
|
universe.Register(leftPaddle);
|
||||||
|
|
||||||
|
IUniverseObject rightPaddle = UniverseObjectFactory.Instantiate().SetUniverseObject("Right Paddle");
|
||||||
|
rightPaddle.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(468f, 0f), scale: new Vector2D(15f, 60f))
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderShape>(Shape2D.Square)
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||||
|
Paddle rightPaddleBehaviour = BehaviourFactory.Instantiate<Paddle>(Keys.Up, Keys.Down, 228f, -228f, 400f);
|
||||||
|
rightPaddleBehaviour.Id = "rp";
|
||||||
|
rightPaddle.BehaviourController.AddBehaviour(rightPaddleBehaviour);
|
||||||
|
universe.Register(rightPaddle);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Wall Top")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(0f, 308f), scale: new Vector2D(552f, 20f))
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderShape>(Shape2D.Square)
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Wall Bottom")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(0f, -308f), scale: new Vector2D(552f, 20f))
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderShape>(Shape2D.Square)
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Wall Right")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(532f, 0f), scale: new Vector2D(20f, 328f))
|
||||||
|
.BehaviourController.AddBehaviour<ScoreWall>((Action)pongManager.ScoreToLeft)
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderShape>(Shape2D.Square)
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("Wall Left")
|
||||||
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(-532f, 0f), scale: new Vector2D(20f, 328f))
|
||||||
|
.BehaviourController.AddBehaviour<ScoreWall>((Action)pongManager.ScoreToRight)
|
||||||
|
.BehaviourController.AddBehaviour<DrawableColliderShape>(Shape2D.Square)
|
||||||
|
.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||||
|
|
||||||
|
return universe;
|
||||||
|
}
|
||||||
|
}
|
16
Shared/Shared.csproj
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LiteNetLib" Version="1.3.1" />
|
||||||
|
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.2.1105">
|
||||||
|
<PrivateAssets>All</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../Engine/Engine/Engine.csproj" />
|
||||||
|
<ProjectReference Include="../Engine/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|