Dependency Injection in Minimal APIs in .Net 6

Minimal Apis in .Net 6 are really an absolutely amazing feature – you can create an API in about 5 or 6 lines of code. If you have a look online, you’ll see a plethora of examples… but unfortunately, they all show you how to write a “Hello World” API.

I’ve recently been playing with these, and initially found the least obvious part to be the DI part. In a controller, you’d simply inject the dependencies into the constructor of the controller; however, the approach is more nuanced with the minimal APIs. Essentially, you use the parameters of the method itself.

Let’s investigate this by looking at a very simple post method:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", () =>
{
    return Results.Ok();
});

app.Run();

This is a cut down version of the code you’ll get from the weather forecast example in the default template. Now, let’s assume that we want to use the IHttpClientFactory from the controller; we can simply do this:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", (IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

As you can see, we’ve injected IHttpClientFactory into our method. This works well, but what if we now want to accept a serialised body in our request; consider the following example:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", (MyClass myClass, IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

We’re passing in MyClass; and, in fact, this will now work – when you run it, you’ll see from swagger that it will expect you to pass the contents of MyClass into the endpoint. “That’s great,” I hear you ask, “But what if I want to register MyClass in the DI and pass that in?” To which I’ll respond: “Well – this actually works out of the box”:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here
builder.Services.AddSingleton<MyClass>(new MyClass() { SomeProperty = "aardvark"});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", (MyClass myClass, IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

The DI is clever enough to determine that you’ve registered a singleton, and so you don’t need that information passing into the endpoint. Again, I hear the reader query this: “All very well, but what if I haven’t registered the class in the DI, but I wanted to and forgot?” Again, I’d respond: “Ah – an excellent question!” You can, indeed force the issue: just as you can use the [FromBody] attribute to insist that the endpoint takes the value from the endpoint body, so you can use the [FromServices] attribute to tell it that you want the class to be resolved from the DI:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient(); // Add HttpClient here
//builder.Services.AddSingleton<MyClass>(new MyClass() { SomeProperty = "aardvark"});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/test", ([FromServices]MyClass myClass, IHttpClientFactory httpClientFactory) =>
{
    var client = httpClientFactory.CreateClient();
    return Results.Ok();
});

app.Run();

The above code will run, and when you call the endpoint, it will crash, telling you that you haven’t told it what you want MyClass to be.

Summary

All pretty nifty if you ask me – it works by default for the 90% case, and for the last 10% there are some simple attributes you can set to force the issue.

I should probably just add that, generally speaking, adding functionality directly to the controller / endpoint method is a bad idea. Most of the time, what you’ll really want to do in the endpoint is simply resolve a service and call a method there.

References

https://benfoster.io/blog/mvc-to-minimal-apis-aspnet-6/

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis

https://www.hanselman.com/blog/exploring-a-minimal-web-api-with-aspnet-core-6

Printing Extended Ascii Characters in Console Apps

I’ve written a fair few articles on using console apps, especially for the purpose of writing games. This post, for example, is the start of a series on creating a snake game using a C# console app.

Disclaimer: this post is largely just bringing together the information listed at the bottom of this article into a single place. I take no credit for the code herein (with the exception of the rectangle itself).

One thing that used to be common knowledge, back when I wrote games and apps in Turbo Pascal and C, was that you could use the extended character set in order to create a rudimentary set of graphics. In this post, I’m going to cover my re-discovery of those characters. We’ll draw a rectangle in a console app.

Back in the Borland days, the way to add these to your program was to hold Alt-Gr and then type the ASCII code. In .Net Framework, these were, broadly, included; however, in .Net Core+, you need to add a NuGet package:

<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />

Once you’ve done this, you need the following magic line to allow you to actually use the code pages:

System.Text.Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

Before we get into using this, let’s see what’s available. Firstly, this is how you would list the code pages:

var encs = CodePagesEncodingProvider.Instance.GetEncodings();
foreach (var enc in encs.OrderBy(a => a.Name))
{
    Console.WriteLine($"{enc.Name} {enc.CodePage}");
}

The specific code page that we’re interested in is IBM437:

Encoding cp437 = Encoding.GetEncoding("IBM437");
byte[] source = new byte[1];
for (byte i = 0x20; i < 0xFE; i++)
{
    source[0] = i;
    Console.WriteLine($"{i}, {cp437.GetString(source)}");
}

We can display a single character like this:

Console.WriteLine($"{cp437.GetString(new byte[1] { 217 })}");

Okay, so we now have all the tools, it’s just assembling them in the right order:

Console.Write($"{cp437.GetString(new byte[1] { 218 })}");
for (int i = 1; i < 20; i++)
{
    Console.Write($"{cp437.GetString(new byte[1] { 196 })}");
}
Console.WriteLine($"{cp437.GetString(new byte[1] { 191 })}");

for (int i = 1; i < 5; i++)
{
    Console.Write($"{cp437.GetString(new byte[1] { 179 })}");
    Console.Write(new String(' ', 19));
    Console.WriteLine($"{cp437.GetString(new byte[1] { 179 })}");
}

Console.Write($"{cp437.GetString(new byte[1] { 192 })}");
for (int i = 1; i < 20; i++)
{
    Console.Write($"{cp437.GetString(new byte[1] { 196 })}");
}
Console.WriteLine($"{cp437.GetString(new byte[1] { 217 })}");

References

https://stackoverflow.com/questions/28005405/how-can-i-turn-on-special-ascii-characters-in-visual-studio

https://stackoverflow.com/questions/58439415/visual-studio-2019-dont-recognize-ascii-characters

https://stackoverflow.com/questions/17619279/extended-ascii-in-c-sharp

https://stackoverflow.com/questions/49215791/vs-code-c-sharp-system-notsupportedexception-no-data-is-available-for-encodin

https://stackoverflow.com/questions/50858209/system-notsupportedexception-no-data-is-available-for-encoding-1252

Running Hangfire with .Net 6 and Lite Storage

One of the things that I’ve been looking into recently is Hangfire. I’ve used this in the past, but not for a while, and so I wanted to get familiar with it again. I imagine there’ll be a couple more posts on the way, but in this one, I’m going to detail how to get Hangfire running using .Net 6, and a Lite DB Storage. I did initially intend to create this with a SQLite DB; however, when I did, I found that the Hangfire messages were sending multiple times; after some searching, I came across this GitHub issue, which suggested the Hangfire.Lite storage.

In this particular article, we’ll look specifically at running Hangfire with an Asp.Net MVC project. In .Net 6, the default template looks like this:

The first step here is that you’ll need some additional refences; here’s the csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">

	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Hangfire.AspNetCore" Version="1.7.27" />
		<PackageReference Include="Hangfire.Core" Version="1.7.27" />
		<PackageReference Include="Hangfire.LiteDB" Version="0.4.1" />
	</ItemGroup>

</Project>

In addition to the Hangfire Core and AspNetCore projects, there’s the LiteDB package.

The next step is, in program.cs:

using Hangfire;
using Hangfire.LiteDB;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

// HF
builder.Services.AddHangfire(configuration =>
{
    configuration.UseLiteDbStorage("./hf.db");    
});
builder.Services.AddHangfireServer();
// HF - End

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
. . .

That’s actually it – that’s all you need to add Hangfire! However, it won’t, technically speaking, do anything. To test, edit the HomeCrontroller.cs:

        public IActionResult Index()
        {
            var id = BackgroundJob.Enqueue(() => DoSomething());
            return View();
        }

        public void DoSomething()
        {
            Console.WriteLine("Test");
            Console.WriteLine($"test2 {Guid.NewGuid()}");
        }

When you run the project, you should see Hangfire fire up and start printing outputs:

When you run it, don’t be too impatient – but it should print the test output.

References

https://github.com/HangfireIO/Hangfire/issues/1025

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-6.0

https://docs.hangfire.io/en/latest/getting-started/index.html

Implementing a Sidecar Pattern Without Kubernetes

A sidecar pattern essentially allows a running process to communicate with a separate process (or sidecar) in order to offload some non-essential functionality. This is typically logging, or something similarly auxiliary to the main function. You tend to find sidecars in Kubernetes clusters (in fact, you can have more than one container in a pod for exactly this reason); however, the pattern is just that: a pattern; and can therefore apply to pretty much anything.

In this post, I’m going to cover how you might set-up two independent containers, and then implement the sidecar pattern using docker compose.

Console App

The code for this is going to be in .Net Core 6, so let’s create a console app:

It will be easier later on if you follow the following directory structure for the projects:

sidecar-test
	console-app
	web-api

For the purpose of the console app, just put it in a parent directory – so the code lives in /sidecar-test/console-app (we’ll come back to the API).

We’ll need to add docker support for this, which you can do in Visual Studio (right click the project and add docker support):

Once you’ve created the docker build file, you may need to edit it slightly; here’s mine:

FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src

COPY ["sidecar-test.csproj", "sidecar-test/"]
WORKDIR "/src/sidecar-test"
RUN dotnet restore "sidecar-test.csproj"
COPY . .

RUN dotnet build "sidecar-test.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "sidecar-test.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "sidecar-test.dll"]

If you find that the dockerfile doesn’t work, then you might find the following posts helpful:

Beginner’s Guide to Docker – Part 2 – Debugging a Docker Build


Beginner’s Guide to Docker – Part 3 – Debugging a Docker Build (Continued)

If you’d rather not wade through that, then the Reader’s Digest version is:

docker build -t sidecar-test .

Once the docker files are building, experiment with running them:

docker run --restart=always -d sidecar-test

Assuming this works, you should see that the output shows Hello World (as per the standard console app).

Web API

For the web API, again, we’re going to go with the default Web API – just create a new project in VS and select ASP.Net Core Web API:

Out of the box, this gives you a Weather Forecaster API, so we’ll stick with that for now, as we’re interested in the pattern itself and not the specifics.

Again, add docker support; here’s mine:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src

COPY ["sidecar-test-api.csproj", "sidecar-test-api/"]
WORKDIR "/src/sidecar-test-api"
RUN dotnet restore "sidecar-test-api.csproj"
COPY . .

RUN dotnet build "sidecar-test-api.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "sidecar-test-api.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "sidecar-test-api.dll"]

You should now be able to build that in a similar fashion as before:

docker build -t sidecar-test-api .

You can now run this, although unlike before, you’ll need to map the ports. The port mapping works by mapping the external port to the internal port – so to connect to the mapping below, you would connect to http://localhost:5001, but that will connect to port 80 inside the container.

docker run --restart=always -d -p 5001:80 sidecar-test

Now that you can connect to the Web API, let’s now implement the sidecar pattern.

Docker Compose

We now have two independent containers; however, they cannot, and do not, communicate. Let’s remind ourselves of the directory structure that we decided on at the start of the post:

sidecar-test
	console-app
	web-api

We now want to open the parent sidecar-test directory. In there, create a file called docker-compose.yml. It should look like this:

version: '3'
services:
  sidecar-api:
    build: .\sidecar-test-sidecar\web-api
    ports: 
      - "5001:80"
  sidecar-program:
    build: .\sidecar-test\console-app
    depends_on: 
      - "sidecar-api"

There’s lots to unpick here. Let’s start with services – this contains all the individual services that we’re going to run in this file; each one has a build section, which tells compose to call docker build on the path specified. You can create the image separately and replace this with an image section. The ports section maps directly to the docker -p switch.

There’s two other important parts of this, this first is the service name (sidecar-api, sidecar-program) – we’ll come back to that shortly. The second is the depends_on – which tells docker compose to ensure that the dependant is built and running first (we could switch the order of these and it would still start the API first).

Running The Sidecar

Finally, we can run our sidecar. You can do this by navigating to the parent sidecar-test directory, and typing:

docker compose up

If you’re changing and running the code, and using PowerShell, you can use the following command:

docker-compose down; docker-compose build --no-cache; docker-compose up

Which will force a rebuild every time.

However, if you run this now, you’ll notice that, whilst it runs, it does nothing.

Linking The Two

Docker Compose has a nifty little feature – all the services in the compose file are networked together, and can be referenced by using their service name.

In the console app, change the main method to:

        public static async Task Main(string[] args)
        {
            Console.WriteLine("Get Weather");
            var httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri("http://sidecar-api");
            var result = await httpClient.GetAsync("WeatherForecast");
            result.EnsureSuccessStatusCode();
            var content = await result.Content.ReadAsStringAsync();
            Console.WriteLine(content);
        }

We won’t focus on the fact that I’m directly instantiating HttpClient here; but notice how we are referencing the API, by http://sidecar-api, and because they are both part of the same compose file, this just works.

If we now recompile the docker files:

docker-compose down; docker-compose build --no-cache; docker-compose up

We should have a running sidecar – admittedly it doesn’t behave like a sidecar, but that’s just why it gets called, not the architecture.

References

https://docs.docker.com/compose/

Beginner’s Guide to Docker – Part 3 – Debugging a Docker Build (Continued)

In this post I starting a series on getting started with Docker; here, I’m going to expand to give some tips on how to debug a docker build when even the build itself is failing.

Imagine that you’re trying to debug a docker build script, and you keep getting build errors. The script never completes, and so you can’t attach to the running container (as shown here).

In this case, there are still a few options available. Let’s consider the following build script:

FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["pcm-test.csproj", "pcm-test/"]
RUN dotnet restore "pcm-test/pcm-test.csproj"
COPY . .
WORKDIR "/src/pcm-test"

RUN dotnet build "pcm-test.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "pcm-test.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "pcm-test.dll"]

When I run this (it’s a console app, btw), I get the following build error:

 => ERROR [build 10/10] RUN dotnet build "pcm-test.csproj" -c Release -o /app/build

#18 0.790 Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET
#18 0.790 Copyright (C) Microsoft Corporation. All rights reserved.
#18 0.790
#18 1.351   Determining projects to restore...
#18 1.623   All projects are up-to-date for restore.
#18 1.703   You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
#18 2.897 CSC : error CS5001: Program does not contain a static 'Main' method suitable for an entry point

I can absolutely assure you that the program does have a main method.

Debugging

When a build goes wrong, I’ve determined two ways to debug – the first is by simply executing debug commands inside the build, and the second is to reduce the build until it works, and then inspect the image.

Executing Debug Commands

Since you can run any command inside a build file, you can simply do something like this:

RUN ls -lrt

Remember that if you do this, you’ll need to change a couple of settings:

–progress

This can be set to auto (default), plain, and tty.

tty (or interactive terminal) and auto will compress the output; whereas plain will show all the container output (including these kind of debug messages.

–no-cache

Docker tries to be clever, and cache the commands that have already executed; however, for debug statements, this is going to mean that they’ll only ever fire the first time. It tells you when it’s executing these from the cache:

docker build -t pcm-test --no-cache --progress=plain .

Sometimes, executing these statements is enough; however, sometimes, it helps to build a partial image.

Removing the breaking parts of the build

When I first started writing software, on a ZX Spectrum – I’d frequently copy (BASIC) code out from a magazine. I didn’t really know what I was writing, so if it didn’t work it would give me an error that I didn’t understand; however, it would tell me the offending line number, and so I’d simply remove that line. Obviously, subsequent lines would then start to fail, and typically, I’d end up with a program that ended at the first typing (or printing) error. This didn’t make for a very good game.

This isn’t true in docker – if you remove the offending code, you can still create an environment, explore it, and even manually execute the rest of the build inside the environment, to see what’s going wrong!

FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["pcm-test.csproj", "pcm-test/"]
RUN dotnet restore "pcm-test/pcm-test.csproj"
COPY . .
WORKDIR "/src/pcm-test"

#RUN dotnet build "pcm-test.csproj" -c Release -o /app/build
#
#FROM build AS publish
#RUN dotnet publish "pcm-test.csproj" -c Release -o /app/publish
#
#FROM base AS final
#WORKDIR /app
#COPY --from=publish /app/publish .
#ENTRYPOINT ["dotnet", "pcm-test.dll"]

This will now create an image, and so we can run and attach to that image:

 docker run -it pcm-test sh

I can now have a look around the image:

As you can see, this looks a bit strange – there’s no code there.

Summary

In this post, I’ve covered two techniques to work out why your docker build may be failing.

Creating a Game in Blazor – Part 4 – Platform and Collision

I’ve recently been creating a JetSet Willy clone in Blazor. I use the term clone loosely – I’m actually not creating a clone or anything like it – I’m simply replicating some aspects of the game (moving, jumping, platforms, collision, etc.).

This is the fourth post in the series that started here. In this post, I’m going to add a platform – so far we’ve been walking on the air.

As with previous posts, the code for this can be found here on GitHub.

In order to do this, we’ll need to do a bit of refactoring, and to introduce a crude collision concept. Let’s start with the refactoring and platform display:

We’re introducing the concept of a base GameObject, from which, Platform, Player, and NPC inherit. We’re dispensibg of IPlayer, and instead, adapting World to manage the elements in the world:

    public class World : IWorld
    {
        private readonly IEnumerable<GameObject> _gameObjects;

        public World(IEnumerable<GameObject> gameObjects)
        {
            _gameObjects = gameObjects;
        }

        public Player Player
        {
            get => (Player)_gameObjects.First(a => a.GameObjectType == GameObjectType.Player);
        }

        public IEnumerable<GameObject> Platforms 
        {  
            get => _gameObjects.Where(a => a.
                GameObjectType == GameObjectType.Platform);
        }

        public void ApplyPhysics()
        {   
            foreach (var gameObject in _gameObjects)
            {
                gameObject.Update();
            }            

        }
        
    }

We’re not dealing with NPCs in this post, but we are dealing with platforms. The new Platform class currently looks like this:

    public class Platform : GameObject
    {
        public Platform(int width, int height, int left, int top)
        {
            Width = width;
            Height = height;
            Left = left;
            Top = top;
            GameObjectType = GameObjectType.Platform;
        }

        public override void Update()
        {
            //throw new System.NotImplementedException();
        }
    }

We’ll no doubt come back to this, but essentially, all we’re doing is maintaining a collection of GameObjects with a specific type.

For now, we’ll inject the properties of the World in through Program.cs:

    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
            builder.Services.AddSingleton<IWorld, World>(srv =>
            {
                var platform = new Platform(
                    50, 10, 0, WorldSettings.FLOOR);

                var player = new Player(17, 21, new[] { platform })
                {
                    GameObjectType = GameObjectType.Player,
                };

                var world = new World(new GameObject[] { player, platform });                
                return world;
            });            
            builder.Services.AddSingleton<IControls, Controls>();
            builder.Services.AddSingleton<IGraphics, Graphics>();

            await builder.Build().RunAsync();
        }
    }

As you can see, we build the Platform and Player and then build the world. Finally, we adapt Game.razor to display the platforms:

@page "/"
@using System.Timers
@using BlazorGame.GameLogic
@inject IEnumerable<GameObject> GameObjects
@inject IControls Controls
@inject IWorld World
@inject IGraphics Graphics

<div @onkeydown="HandleKeyDown" @onkeyup="HandleKeyUp" @onkeydown:preventDefault 
    style="background-color: #000000; width: 80vw; height: 80vh; margin: auto"
    tabindex="0" @ref="mainDiv">
    <div style="color: white; top: @(World.Player.Top)px; left: @(World.Player.Left)px; width: @(World.Player.Width)px; height: @(World.Player.Height)px; overflow: hidden; position: relative">
        <img 
            src="/images/Willy-Sprite-Sheet.png" 
            style="margin: 0 @(Graphics.PlayerOffset)px; transform: scaleX(@(Graphics.PlayerDirection))" />
    </div>

    @foreach (var platform in World.Platforms)
    {
        <div style="position: relative; top:@(platform.Top)px; left:@(platform.Left)px; width:@(platform.Width)px; height:@(platform.Height)px; border: 1px solid #FFFFFF; background-color: #FFFFFF"></div>
    }
</div>

Finally, we need to ensure that our player lands on the platform (and doesn’t simply drop through). For now, we’ll pass the World into Player and perform the logic there:

        public override void Update()
        {
            _forceUp -= WorldSettings.GRAVITY;

            Top -= _forceUp;

            Console.WriteLine($"Top: {Top}, Left: {Left}, Width: {Width}");

            var platform = _gameObjects.FirstOrDefault(a =>
                a.Top <= Top &&
                a.Left <= Left + Width &&
                a.Left + a.Width >= Left + Width &&
                a.GameObjectType == GameObjectType.Platform);
            
            if (platform != null)
            {
                Top = platform.Top;
                _forceUp = 0;
            }
            
            if (Left <= 0 && _forceRight < 0) 
                _forceRight = 0;
            else if (_forceRight != 0)
                _direction = _forceRight;            

            Left += _forceRight;
            
        }

It’s not quite finished yet, but we now have a platform that we can walk on, and fall off:

What’s Next?

In the next post, I’ll try to introduce an NPC (not quite sure where I can get the graphics from yet, so it might just be a rectangle or something).

Creating a Game in Blazor – Part 3 – Graphics

In this post I started writing a game in Blazor. In this last post, I covered how we could use the keyboard to move an object around, and how we could apply gravity.

In this post, we’re going to refactor, and we’re going to replace the word test with something approximating Willy.

Refactor

Just because we’re writing a game in Blazor is no reason not to use the IoC container in order to better structure the code. I’m not going to cover all of the refactoring here; however, the changes are here.

We’ve added a sub-directory called GameLogic which contains all the relevant classes:

At some point in the future, we may separate this directory into its own project; but for now, we have four classes:

Controls – this handles the user input.

Graphics – this will handle the manipulation of the graphics.

Player – this handles behaviour of the player.

World – this deals with the things such as collisions, gravity, etc.

WorldSettings – these are just a list of variables that control how things move. In the original game, there was a POKE that meant you could jump so high that you went into the next room.

I won’t cover what’s actually in these classes – it’s essentially what was in the main Razor file. I will cover the change to the razor file itself:

@page "/"
@using System.Timers
@using BlazorGame.GameLogic
@inject IPlayer Player
@inject IControls Controls
@inject IWorld World

<div @onkeydown="HandleKeyDown" @onkeyup="HandleKeyUp" @onkeydown:preventDefault 
    style="background-color: #000000; width: 80vw; height: 80vh; margin: auto"
    tabindex="0" @ref="mainDiv">
    <div style="color: white; top: @(Player.Top)px; left: @(Player.Left)px; width: 20px; position: relative">test</div>
</div>

@code {

    private ElementReference mainDiv;    
    private Timer _timer;

    private void HandleKeyUp(KeyboardEventArgs e) =>
        Controls.KeyUp(e.Code);    

    private void HandleKeyDown(KeyboardEventArgs e) =>    
        Controls.KeyDown(e.Code);    

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await mainDiv.FocusAsync();
        }
    }

    protected override Task OnInitializedAsync()
    {
        _timer = new Timer();
        _timer.Interval = 16;
        _timer.Elapsed += TimerElapsed;
        _timer.AutoReset = true;
        _timer.Enabled = true;        

        return base.OnInitializedAsync();
    }

    private void TimerElapsed(Object source, System.Timers.ElapsedEventArgs e)
    {
        Update();
        Draw();
    }

    private void Update()
    {
        World.ApplyPhysics();
    }

    private void Draw() => 
        this.StateHasChanged();    

}

This is much more terse than before, and delegates most of its functionality to the classes that we described above. You’ll see at the top that we @inject those classes into the page.

Finally, the classes are registered in Program.cs:

        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
            builder.Services.AddSingleton<IWorld, World>();
            builder.Services.AddSingleton<IPlayer, Player>();
            builder.Services.AddSingleton<IControls, Controls>();

            await builder.Build().RunAsync();
        }

This is by no means the last refactoring that we’ll do, but it’s perhaps the last one that will make it into its own section of a post.

Graphics

For the graphics, I spent a while trying to get various graphics libraries to work cross platform. I finally realised that, not only did I not need a graphics library, but that I’d solved this issue before – well, more or less. The answer was to use CSS to animate the image. The very first step was to add a sprite sheet; which I got from here, and since Jet Set Willy is the same character as manic miner (with just one pixel difference in the hat), I managed to add a sprite sheet:

The next change was to alter the HTML in Game.razor slightly:

<div @onkeydown="HandleKeyDown" @onkeyup="HandleKeyUp" @onkeydown:preventDefault 
    style="background-color: #000000; width: 80vw; height: 80vh; margin: auto"
    tabindex="0" @ref="mainDiv">
    <div style="color: white; top: @(Player.Top)px; left: @(Player.Left)px; width: 16px; height: 17px; overflow: hidden; position: relative">
        <img 
            src="/images/Willy-Sprite-Sheet.png" 
            style="margin: 0 @(Graphics.PlayerOffset)px; transform: scaleX(@(Graphics.PlayerDirection))" />
    </div>
</div>

There’s a few things to unpick here. Let’s start with the interaction between the div and the img. Essentially, we’re using the div as a window into the image; similar to this:

Both the margin and transform are set to bound properties of a new Graphics class, which we’ll come to in a second; but first, let’s see the other change to this file:

    private void Update()
    {
        World.ApplyPhysics();
        if (Player.IsWalking)
        {
            Graphics.CyclePlayer();
        }
    }

This allows us to change the bound variables that we mentioned earlier.

Now that we’ve seen the changes to the main razor file, let’s see the new Graphics class:

    public class Graphics : IGraphics
    {
        private readonly IPlayer _player;
        private int _playerOffset = 0;
        private DateTime _lastUpdate = DateTime.MinValue;

        public Graphics(IPlayer player)
        {
            _player = player;
        }

        public int PlayerOffset => _playerOffset;

        public int PlayerDirection =>
            _player switch
            {
                { IsWalkingLeft: true } => -1,
                { IsWalkingRight: true } => 1,
                _ => 0
            };
        
        public void CyclePlayer()
        {
            if (_lastUpdate.AddMilliseconds(100) > DateTime.Now) return;

            if (_playerOffset > -48)
                _playerOffset -= 16;
            else
                _playerOffset = 0;

            _lastUpdate = DateTime.Now;
        }
    }

This is essentially a utility, or helper class. It encapsulates details about the graphics that are displayed, and uses the Player class to do so. Most of it is relatively self-explanatory, with the possible exception of CyclePlayer which moves the offset that we mentioned earlier no more frequently that every 100ms.

That’s pretty much it; we now have a walking Willy:

What’s Next?

In the next post, we’ll try to add a platform, and some collision logic.

References

https://www.spriters-resource.com/fullview/113060/

https://gunnarpeipman.com/csharp-reading-embedded-files/

https://www.hanselman.com/blog/how-do-you-use-systemdrawing-in-net-core

https://www.davidguida.net/blazor-gamedev-part-11-improved-assets-loading/

https://stackoverflow.com/questions/493296/css-display-an-image-resized-and-cropped

11 Things that Hollywood has Taught us About Programming, Hacking, and General Computer Use

Ever watched 24, The Blacklist, War Games, or basically anything where a computer, or technology is being used? You’ll know these…

1. If your system is being hacked, don’t try to ascertain what’s happening, or exactly how someone has breached your system. Instead, type as fast as is physically possible, or even faster. Every time the hacker makes a “move”, this will enable you to make a counter “move”. Unfortunately, once you’re in such a “hack battle”, you’re destined to lose – after all, there’s no drama in a failed hack.

2. Shortly after the “hack” all the monitors in the building will turn off. It’s unclear exactly why this happens, but it will make sense at the time.

3. If you need to copy data onto any form of external media for either the purpose of espionage or crime fighting, the time that the data takes to copy can be calculated thus:

Time it takes for the owner of the targeted machine to return to the room – 1 second

4. If you need to access an account (it doesn’t matter what the account is for – Nuclear launch codes, personal PC, police forensic database, your child’s Facebook page, anything), then the password will be set to a relatively short and easily guessable word that relates directly to the account holder. For example, daughter’s name, lost love, dead son, etc.

5. While doing anything on a computer, you will never, ever see any apps appear that are not relevant to the task in hand. For example, you’ll never see a Lenovo popup, or a Windows update message. It’s also very likely that the application that you need will magically appear on the screen.

6. At no point, ever, will you experience a hardware glitch that is not relevant to the task at hand. You will, for example, never find that a monitor stops working, or the keyboard fails.

7. When doing complex processing, such as facial recognition, fingerprint matching, etc., it is necessary to display images onto the screen rapidly; however, only around 5 or 6 faces, fingerprints, or whatever you’re searching for, will actually need to be scanned before the correct answer is found. As a result, the speed can be estimated to be the length of time it takes the character sat at the machine to say the following words:

“I put this into our [fingerprint, facial recognition, stolen diamond] database and…”

8. You will never forget the syntax of any command, and you will never, ever need to refer to Google, a book, or any other person to help you with said syntax. You will simply know the exact syntax for every single command.

9. You will never, ever make any form of typing error – in fact, the only reason for the delete key on the keyboard is to dramatically delete files that would be incriminating.

10. Deleted files can always be recovered, even if the person that deleted them “knew what they were doing”. However, the recovery must be undertaken by an under-appreciated, and overworked “IT Guy”. Any other attempt to retrieve files deleted in (9) will fail.

11. Every single person that works in American law enforcement under the age of 35 is an absolute guru with anything electronic, and will only ever use an Apple Mac or a Windows 8 tablet. Anyone over the age of 35 in American law enforcement has never seen a computer before, and will be unable to turn one on.

Creating a Game in Blazor – Part 2 – Gravity, and Controls

In this post I started the process of writing a very vague approximation of Jet Set Willy, in an effort to regain my youth.

Here, we’re going to take those very vague foundations, and allow left and right controls, along with a jump function. As a caveat, I’m going to state here that I expect quite large swathes of this code to change significantly before I declare it complete; and, as another caveat, there were 60 rooms in the original Jet Set Willy. I’ll be surprised if this series of posts manages to replicate the bathroom (which is the first room).

From the first post, we have a Game.razor, and all the code for this post will go into there. You can simply view that code here.

I’ve introduced a game loop here, which calls an update and then a draw method. Currently, the game loop is just a crude timer:

    private void TimerElapsed(Object source, System.Timers.ElapsedEventArgs e)
    {
        Update();
        Draw();
    }

This gets registered in the OnInitializedAsync method:

    protected override Task OnInitializedAsync()
    {
        _timer = new Timer();
        _timer.Interval = 16;
        _timer.Elapsed += TimerElapsed;
        _timer.AutoReset = true;
        _timer.Enabled = true;        

        return base.OnInitializedAsync();
    }

The Draw method is, in fact, just a single line:

private void Draw() => this.StateHasChanged();  

The reason for this is that Blazor doesn’t always automatically refresh state – updating bound objects on a timer is one of the times when it doesn’t. The Update method has a little more to it:

    private void Update()
    {
        ApplyGravity();
        _top -= _forceUp;

        if (_top > FLOOR)
        {
            _top = FLOOR;
            _forceUp = 0;
        }

        if (_left <= 0 && _forceRight < 0) _forceRight = 0;

        _left += _forceRight;
    }

Anything in caps is just a constant. The rest of the code basically takes care of the left, right, and jump.

Next, we’ll need to modify the HandleKeyDown method:

    private void HandleKeyDown(KeyboardEventArgs e)
    {
        switch (e.Code)
        {
            case "ArrowLeft": // Left
                if (_forceUp == 0)
                {
                    Walk(-SPEED);
                }
                break;
            case "ArrowUp": // Up
                Jump();
                break;
            case "ArrowRight": // Right
                if (_forceUp == 0)
                {
                    Walk(SPEED);
                }
                break;
            default:
                break;
        }
    }

As you can see, we’ve added Jump and Walk methods. All the logic has moved into those methods:

    private void Walk(int amt) => _forceRight = amt;

    private void Jump()
    {
        if (_forceUp == 0)
        {
            _forceUp += JUMP_FORCE;
        }
    }

Since Walk needs to be immediate, we just set the _forceRight to whatever value it’s passed; similarly with the jump, although we can’t allow a double jump, so we determine if a jump is in progress first.

Finally, we’ve added a HandleKeyUp: this stop walking when the key is released.

    private void HandleKeyUp(KeyboardEventArgs e)
    {
        switch (e.Code)
        {
            case "ArrowLeft": // Left
                Walk(0);
                break;

            case "ArrowRight": // Right
                Walk(0);
                break;

            default:
                break;
        }
    }

If you run this, you should now be able to walk “test” around the screen, and cause him to jump.

What’s Next?

For the next steps, I’m going to add a platform, and try to use some graphics. I’ll also try to refactor a little, as the main form is becoming a little large.

References

https://docs.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-5.0

https://www.pmichaels.net/2019/06/03/creating-a-car-game-in-react-part-1-drawing-and-moving/

https://stackoverflow.com/questions/58920461/how-to-detect-key-press-without-using-an-input-tag-in-blazor

Creating a Game in Blazor – Part 1 – Moving Objects

By now, writing games in frameworks that were clearly not designed for such things is becoming something of a habit for me. Here I created a poor-man’s version of Trans-Am (the hugely popular Spectrum game) in React, and here I attempted something along the lines of Daley Thompson’s Decathlon in Vue. I even created a catch game and a snake game in a .Net console app.

In this post, I’m going to attempt to create a game in Blazor. I’m hoping I can get a vague approximation of a platform game, such as Jet Set Willy. Obviously, this will not be as advanced (nor will I be able to offer a helicopter ride as a prize for finishing, as the creators originally did), but I can hopefully get some semblance of something moving across the screen, and the ability to jump over obstacles.

For part one, we’ll just explore how we would move an object. To start, let’s create a default Blazor client side application.

If you’d like to see the finished product from this post, you’ll find it here.

We’ll start in the Counter.razor file. We’ll simply replace the existing code with the following:

@page "/counter"

<h1>Counter</h1>

<button class="btn btn-primary" @onclick="IncrementLeft">Across</button>
<button class="btn btn-primary" @onclick="IncrementTop">Down</button>

<div style="top: @(top)px; left: @(left)px; position: absolute">test</div>

@code {
private int top = 0;
private int left = 0;

private void IncrementLeft()
{
left += 10;
}

private void IncrementTop()
{
top += 10;
}

}

All we’re really doing here is binding the position of the div to the variables left and top and then changing them on the button presses. Each time they are updated, they trigger a refresh of the screen.

You should now see the word test (very vaguely) in the top left hand corner of the screen. Pressing the Across and Down buttons move the text – if you move it far enough, you should be able to see it clearly against the white background.

Tidy Up

Now that this is working, let’s remove some of the cruft – our game doesn’t need a menu, or a FetchData screen; the following files can be removed:

Index.razor
NavMenu.razor
FetchData.razor

In Counter.razor we can change the routing:

@page "/"

Finally, change the code in MainLayout.razor to the following:

@inherits LayoutComponentBase

<div class="page">
<div class="main">
<div class="content px-4">
@Body
</div>
</div>
</div>

What’s left is just the counter screen: which can now be renamed to something more appropriate – I’ve changed mine to Game.razor.

What’s Next

We now have something moving around the screen – the next step will be to make this work using key presses, and add an actual platform for our game.