Code Monkey home page Code Monkey logo

bloodmasters's Introduction

Bloodmasters

Bloodmasters is an old-school multiplayer top-down arcade shooter. Originally developed by Pascal vd Heiden in 2006–2008, this is now under active restoration.

Read more on the official website.

System Requirements

For now, only Windows is supported (we are working on cross-platform support, see issue #20 for details). If you want to see the game on another platform, feel free to open an issue.

The game is published as a self-contained .NET 7 executable, and doesn't require any additional dependencies.

How to Play

Stable Version

Download the latest installer from the Releases section.

Development Version

Download the game package (Bloodmasters.zip) from the latest CI build. Unpack, run with Bloodmasters.exe.

Documentation

bloodmasters's People

Contributors

bookuha avatar fornever avatar keterscp avatar klapstoelpiloot avatar qeezqeez avatar readys avatar renovate[bot] avatar shockthunder avatar sotiredofyours avatar sourcesurfer avatar vladesko avatar y0ung3r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

bloodmasters's Issues

Move server to a common solution

The server app should be moved into a common solution file with everything else. Currently, the server lives in a separate solution because it has its own set of solution configurations (Windows and Linux). Maybe figure them out was well while we are at it.

Migrate away from the all-purpose directory

After #81, we now have a number of entries in the common Paths class for various use in the game.

All the entries are directed to the one all-purpose directory currently, though. (In dev mode, it is the infamous Build, and in other modes, it is the app binary folder.)

We should use the right folders, such as %APPDATA% and such.

Static content should only be in read-only folders, while dynamically downloaded content should be in DownloadsDirPath.

See CodeImp.Bloodmasters.Paths.AllPurposeDirPath in the code.

Black square stays after exiting the game

  1. Start the game using Bloodmasters.Client default configuration
  2. It will start launcher
  3. Use launcher to start the game
  4. Play the game
  5. Exit the game
  6. You'll see a black square. Here, it is visible on top of the launcher window.
image

The game process is still alive at that moment.

Here's the parallel stacks window in case that helps:
image

It looks like there's an outstanding call to SynchronizationContext.Send somewhere that gets stuck and blocks the runtime from terminating.

Review `Vector3Ex::Project` and `Vector3Ex::Unproject`

Original code was calling the corresponding functions on vectors in Managed DirectX, but SharpDX doesn't have such overloads. I had to combine the projection, view and world matrices myself, but I've no idea what I am doing :)

See // TODO[#15] and review the corresponding code.

  • Vector3Ex::Project
  • Vector3Ex::Unproject

Exception from `Sort` in `PhysicsState.ApplyVelocity`

Occasionally, the following exception gets thrown on the client:

System.Private.CoreLib throws ArgumentException:
Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'System.Collections.Comparer'.
   at System.Array.SorterObjectArray.IntrospectiveSort(Int32 left, Int32 length)
   at System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, IComparer comparer)
   at System.Collections.ArrayList.Sort(Int32 index, Int32 count, IComparer comparer)
   at System.Collections.ArrayList.Sort()
   at CodeImp.Bloodmasters.PhysicsState.ApplyVelocity(Boolean wallcollisions, Boolean clientcollisions, ArrayList allclients, IPhysicsState thisclient, Sidedef& crossline, Object& hitobj) in G:\Projects\bloodmasters\Source\Shared\PhysicsState.cs:line 213
   at CodeImp.Bloodmasters.PhysicsState.ApplyVelocity(Boolean wallcollisions, Boolean clientcollisions, ArrayList allclients, IPhysicsState thisclient, Sidedef& crossline) in G:\Projects\bloodmasters\Source\Shared\PhysicsState.cs:line 139
   at CodeImp.Bloodmasters.Client.Actor.ProcessMovement() in G:\Projects\bloodmasters\Source\Client\General\Actor.cs:line 959
   at CodeImp.Bloodmasters.Client.Actor.Process() in G:\Projects\bloodmasters\Source\Client\General\Actor.cs:line 973
   at CodeImp.Bloodmasters.Client.Arena.Process() in G:\Projects\bloodmasters\Source\Client\General\Arena.cs:line 1094
   at CodeImp.Bloodmasters.Client.General.DoOneFrame(Boolean process, Boolean render, Boolean renderhud) in G:\Projects\bloodmasters\Source\Client\General\General.cs:line 1775
   at CodeImp.Bloodmasters.Client.General.GeneralLoop() in G:\Projects\bloodmasters\Source\Client\General\General.cs:line 1728
   at CodeImp.Bloodmasters.Client.General.StartGame() in G:\Projects\bloodmasters\Source\Client\General\General.cs:line 2421
   at CodeImp.Bloodmasters.Client.General._Main(String[] args) in G:\Projects\bloodmasters\Source\Client\General\General.cs:line 2297
   at CodeImp.Bloodmasters.Client.General.Main(String[] args) in G:\Projects\bloodmasters\Source\Client\General\General.cs:line 2172


Startup error about DirectX DLL

privately reported problem:
image_2023-07-10_19-50-24

Probably we need DirectX 9 installed on the user's computer, or something like that?

TODO

  • Bundle the libraries for use in dev mode
    • If impossible to bundle, figure out how to install, and add the information into the contributor guide
  • Make sure the libraries are also available in the installed version of the game
    • Either open a new task for installer, or make sure they get bundled by either dotnet build or dotnet pack properly
  • If we figure out anything new, add our information to this old question on Stack Overflow

Figure out what about glbsp binary

glbsp is a binary we currently embed into the game. We should figure out how's it used and if it's possible to get rid of it, or build from sources, or do something.

Fix namespaces in the code

Currently, most of our code lives in CodeImp.Bloodmasters, but certain parts are in just Bloodmasters.

Also, not all the namespaces correspond to the file layout.

Let's fix all this.

TODO

  • #10
  • Fix most namespaces
  • Fix the namespaces in Launcher
  • Make sure there are no folders that are not namespace providers left

Allow overriding dev mode in tests

Since we now have tests that may check stuff for dev and non-dev mode separately (#96), let's have some way of overriding that.

I imagine two solutions.

  1. The hacky one (but quick to implement). Make Paths.IsDevMode a writable property, add an internal method WithDevMode, that will be used like this:
    void Test() {
        Paths.WithDevMode(true, () => {
            // do something with paths knowing that IsDevMode = true
        });
    }
  2. The good one (but harder to implement): make Paths a normal non-static class, inject (or simply pass) it into the components that want it, allow to pass isDevMode into an internal constructor.
  3. The mixed one. Make Paths a singleton with an internally-available constructor. Migrate the production code to Paths.Instance, and the test code to new Paths(isDevMode: true/false).

Seek for resources in both bundled and downloaded resources directory

Currently, we allow auto-downloading of certain content to the Paths.DownloadsDirPath, but resource lookup only looks in Paths.BundlerResourceDirPath.

  1. Figure out what content gets downloaded exactly: only maps or anything else?
  2. Make sure we enumerate the file system properly when looking up maps and other kinds of downloadable content: we should look in both bundled resource dir and in the downloads folder.

Think about the game architecture

In #27, I had to implement some ad-hoc "architecture", if you will. Before that, certain classes were included in several projects and compiled using conditional compilation (#if CLIENT, #if SERVER, #if LAUNCHER).

I have simply converted each of such cases into a hierarchy of classes and sometimes interfaces.

In particular, the following items were touched and/or introduced during the process:

  • ClientCollision
  • ClientPhysicsState
  • ClientWallCollision
  • ClientPlayerCollision
  • ClientMap
  • ClientSector
  • ClientSidedef
  • ClientGateway
  • SharedGeneral
  • ServerPhysicsState
  • ServerPlayerCollision
  • ServerWallCollision
  • ServerGateway
  • ServerMap
  • ServerSector
  • IClientCollision

In places that were expecting specific implementations, I have simply added type casts, and extracted class factory methods sometimes.

All this is highly dubious. Especially the type casts. They have worse performance than the linear code, and also they are pretty ugly and tend to fail in runtime if not used correctly.

We'll have to review this architecture and implement a better architectural solution in each particular case.

For the General classes, I think we can make them non-static and use DI.

For everything else, I haven't still decided. Any advice is welcome.

Support unrar across all the platforms

Bloodmasters seems to use RAR for… some purposes.

Before merge of #27, there was separate support for RAR on Windows and Linux, but I've removed a corresponding conditional compilation block and made Linux implementation to try loading a Windows library (which will obviously fail).

Notably, it would also fail earlier, because the Linux implementation was defunct (wasn't loading any libraries).

We should bring some kind of cross-platform library for RAR and get rid of native libraries (and, while we are at it, also remove the corresponding binaries from the Build directory).

See #42 in the code when performing this operation, I've left a comment where to start.

Launcher: wrong layout on HiDPI

  1. Start the Bloodmasters project (Note: this is not the launcher, this is the game itself).
  2. It will start the launcher process in some unusual mode (HiDPI override?) which
    • is drawn much more crispy than the default run mode. See this image: old on the right, new on the left
      image
    • has several layout glitches:
      image
      image

We'll need to figure out why does it has different DPI modes when started directly from the IDE or from the game process, and fix the hidpi layouting issue (and maybe just make it work in HiDPI always).

Port to cross-platform API

Eventually, after fixing all of the migration issues, I want to make Bloodmasters cross-platform, using Raylib or Monogame or something else.

But before that, we need to make sure everything works properly on the old DirectX. This means the following issues have to be resolved:

Also, the following infrastructure tasks have to be resolved:

Laser sight does not correspond to actual bullet trajectory when facing east

When you face west, the bullets follow the exact path that is expected by the Laser sight: GIF
But once you face east, the bullets stop following the path of the laser sight: GIF

It feels like the game makes the player switch hands to handle the gun, but it is not visually reflected. (or just game shouldn't make player switch hands)

Use generic collections across project

Currently, there is heavy usage of non-generic collections like ArrayList or Hashtable (due to the fact that the original game was written in .NET Framework 1.1).

We should migrate them to generic versions (like List<T> or Dictionary<TKey, TValue>). This will improve both code readability and reliability.

Improve the debug configuration for the game client

Currently, it's really hard to debug the client, because it will start the launcher and terminate, and thus the debugger will detach.

I propose us to create a static config file for the client and two launch setting configurations:

  • start it normally (to check the launcher starting logic)
  • start using the static config

See // TODO[#13] in the code for the previous approach on debug.

Clean up the binaries, maps and whatnot

Currently, we have a lot of various files of unclear origin in the repository: various parts of map files and even copied binaries in the Build directory.

We need to sort that out.

Depends on:

TODO:

  • While we are at it, make the project outputs to be placed in the default location

Revive the sound system

It was too tedious to convert the sound system from Managed DirectX to SharpDX (because the functions to load sounds from disk are not available in SharpDX for DirectAudio, it seems?), so for now I've disabled the sound.

See // TODO[#16], restore the sound (possibly using NAudio instead of DirectSound) and make it work again.

Consider reworking configuration management

Currently, the configuration is stored in .cfg file and is managed by a custom Configuration class for read/write/save operations.

We should consider reworking this to use a modern file format (json?) and eliminate most of the code in this file. Maybe even inject config from DI

Return custom and fullscreen mode

Currently, the game client crashes if it's started in fullscreen. We should investigate that.

Also, I've temporarily locked the resolution; we should unlock it back.

See // TODO[#14] in the code.

Document the mapping

We need to figure out how exactly the maps were created.

There's a document on mapping here, but it may be outdated.

Also, since #78, the mappers are also required to run glbsp on their maps.

Let's see what of this may be simplified (i.e. a pre-packaged Doom Builder installation or whatever), and also let's see into glbsp usage and whether it's possible to repack the relevant parts with Cesium, for the game to pick up maps from the editor without manual run of glbsp.

Cross-platform `GetCurrentTime()`

After #27, our current implementation of GetCurrentTime() relies on Windows-only QueryPerformanceCounter.

We should investigate what's the modern approach to timing on .NET (perhaps there's already an internal implementation that uses QueryPerformanceCounter on Windows and something similarly precise on other platforms?), or roll up our own for now.

Timing is used in server, so it should be of good quality even before we have a cross-platform client.

See TODO[#43] when fixing the issue.

Get rid of `General.temppath`

Currently, all three of our executables use General.temppath.

We should migrate that to the common Paths class, and make sure we use a correct modern approach to temp folder creation.

Fix up `Client.General.logtofile`

We have an internal flag CodeImp.Bloodmasters.Client.General.logtofile that's only used in the server code, and a corresponding field CodeImp.Bloodmasters.Client.General.logfilename.

Let's do something appropriate with it, like inject a dependency with the flag to the corresponding server components, or something.

See #45 in the code to locate the flag.

Review `Arena::RenderObjectsPass`

See // TODO[#12]: in the original code, there was a call

Direct3D.d3dd.SetStreamSource(0, Shadow.vertices, 0)

Which seemingly calculated the size of vertex type instance under the covers:

// (decompiled)
if (streamData != null)
  num = streamData.TypeSize;
// ISSUE: cast to a function pointer type
// ISSUE: function pointer call
int resultCode = __calli((__FnPtr<int (int, uint, IDirect3DVertexBuffer9*, uint, uint)>) *(int*) (*(int*) this.m_lpUM + 400))((uint) this.m_lpUM, (uint) streamNumber, lpUm, (uint) offsetInBytes, num);

But now, there's another call:

Direct3D.d3dd.SetStreamSource(0, Shadow.vertices, 0, Shadow.vertices.Description.SizeInBytes);

Is this SizeInBytes size of one vertex item or the whole buffer?

Project file cleanup

  • Extract common files into separate project, get rid of #if
    • Shared
    • Server
    • Client
    • Final sweep
  • Get rid of <DefineConstants>
  • Remove manually inserted .cs files, use wildcard from SDK
  • Figure out the output directory properly
  • Extract the Server code from the client

New user-writeable directory structure

Currently, we have several folders in Paths that should be user-writable:

  • DownloadedResourceDir
  • ConfigDirPath
  • LogDirPath
  • ScreenshotsDirPath

Let's move them accordingly.

  • DownloadedResourceDir: to the persistent app data folder since that's the user content
    • Maybe something like Documents\My Games on Windows?
    • $XDG_DATA_HOME/bloodmasters aka ~/.local/share/bloodmasters on Linux
    • ~/Library/Bloodmasters on macOS
  • ConfigDirPath
    • %APPDATA%\Bloodmasters on Windows
    • $XDG_CONFIG_HOME/bloodmasters aka ~/.config/bloodmasters on Linux
    • ~/Library/Preferences/bloodmasters on macOS
  • LogDirPath
    • %TEMP%\Bloodmasters\Logs\ on Windows
    • $XDG_STATE_HOME/bloodmasters aka ~/.local/state on Linux
    • ~/Library/Logs/Bloodmasters on macOS
  • Make sure there's a reasonable log cleanup policy / size limit
  • On the server, just log to stdout by default and make the logging system configurable
  • ScreenshotsDirPath
    • %USERPROFILE%\Pictures\Screenshots on Windows
    • $XDG_PICTURES_DIR/Bloodmasters aka ~/Pictures/Bloodmasters
    • Desktop folder on macOS
  • Make sure the server dirs are configurable
  • Document all that

Client installer

Depends on

TODO

  • Analyze if we want to create an installer, or will xcopy deployment be enough for us
    • This depends on the result of #29, whether we need some kind of DirectX redistributable with us or not
  • If we need, then either resurrect Inno Setup files or write a new one with WiX

CI: add Linux and macOS

Currently, Bloodmasters only supports Windows, but we can already enable alternative operating systems to run tests on them. Our server should be cross-platform as well.

Prepare distribution

Prepare the game distribution (client + launcher, server) for each build.

TODO

  • Dev mode build
    • Make Client work from default bin dir
    • Make Launcher work from default bin dir
    • Make DedicatedServer work from default bin dir
  • Extract Build/* into a separate Content project
    • On build, do nothing
    • On pack, use a local RAR archiver
      • Or even one we will write?
  • Ideally, dotnet publish should prepare a full game distribution
  • Upload those packs to GitHub Actions
  • Document how to get the latest version of the game

Resources directory sweep

We need to figure out what we have in the Resources directory, and either integrate with the build system or get rid of it.

Migrate to .NET SDK

After our migration to SharpDX, Visual Studio should no longer be required (theoretically) to build the project.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.