2024-11-18

Good article going into the details of NAT-traversal

Found this excellent article that describes in detail how NAT-traversal work.

I hadn't consider the 'virtual' aspect of how we open up communication channels. It all makes sense, good thing this article was written in such a great way!

 https://tailscale.com/blog/how-nat-traversal-works

2024-11-06

Stack allocated BitArray in C#

I've been playing around a bit with trying to get a value typed struct with an 'array like data load' into a good state where I feel happy using it. I'll let this serve as a template for future reference to highlight what the syntax look like.

Beware that using the 'in' parameter comes with a caveat. C# treats all methods as 'maybe mutable' and hence will do a defensive copy on the referenced object prior to invoking the function.

The alternative is to use ref, but then you are cluttering the invocation.

Example usage with static anonymous function to avoid lambda allocation:

hits = new int[3];
var container = new List<int>();
range.ForeachMarkedIndexWithCount(hits, container, static (entityIndex, hits, container, i) =>
{
hits[i] = entityIndex;
container.Add(entityIndex);
});

range.CombineAndMaskWith(combine, mask);


Code-snippet using unsafe struct, with fixed ulong words with constant size etc

 public unsafe struct NetIndexRange {

private const int BitsPerWord = 64;
private const int NumWords = (NetConstants.NumMaxNetEntities + BitsPerWord - 1) / BitsPerWord; // Ceiling of for example : 1000/64 = 16

private fixed ulong _words[NumWords];

private ref ulong ModifyWord(int index)
{
return ref _words[index];
}
public void MarkIndex(int index)
{
int wordIndex = index / 64;
int bitPosition = index % 64;
ulong mask = 1UL << bitPosition;

ModifyWord(wordIndex) |= mask;
}

public void ClearIndex(int index)
{
int wordIndex = index / 64;
int bitPosition = index % 64;
ulong mask = ~(1UL << bitPosition);

ModifyWord(wordIndex) &= mask;
}

public void CombineAndMaskWith(in NetIndexRange combine, in NetIndexRange mask)
{
for (int wordIndex = 0; wordIndex < NumWords; ++wordIndex)
{
ulong combinedWord = _words[wordIndex] | combine._words[wordIndex];
ulong maskWord = mask._words[wordIndex];
ulong result = combinedWord & maskWord;

ModifyWord(wordIndex) = result;
}
}

public void ForeachMarkedIndexWithCount<T, U>(T t, U u, Action<int, T, U, int> eachMarkedIndexAction) 
where T : class
where U : class
{
int count = 0;
int entityIndex = 0;

try
{
for (int wordIndex = 0; wordIndex < NumWords; wordIndex++)
{
ulong word = ModifyWord(wordIndex);
int bitPosition = 0;

while (word != 0)
{
if ((word & 1UL) != 0)
{
entityIndex = (wordIndex * 64) + bitPosition;
eachMarkedIndexAction(entityIndex, t, u, count++);
}
word >>= 1;
bitPosition++;
}
}
}
catch (Exception e)
{
throw new Exception($"Error while invoking action on net_{entityIndex} {Environment.NewLine} OriginalException: {e}");
}
}

2024-11-01

Is there an issue with this code, is there an issue with me, is there an issue with software?

Scenario: someone asks for feedback on this piece of code where they want to centralize object instantiation to solve the problem. In their mind they use defensive programming to make sure we do not cause errors. The person is driven and likes to communicate.

In a team, we are asked to strive to reach consensus for how to tackle technical challenges and encourage collaboration and open discussions. 

Everyones opinion carries equal weight, in discussion about the review someone on the team says that the nested if-statements makes the code hard to read and suggests that we break them into early returns instead.

Someone read an article about SOLID and suggests that it tries to tackle too many responsibilities and suggests that we break logic into standalone functions with clearer names such as TryToAddDependencyIfParent(...). Everyone on the team seems happy with contributing to making this code great.

I suggest that this code is missing the bigger picture, that we should strive to make object lifetime deterministic. That we need to be clear on errors and what happens when we don't follow the happy path. 

People on the team don't understand.I phrased it wrong, it didn't get enough traction. Someone believes they understand my concern, and suggests we change the function to bool TryCreateEntity(...). The Team is happy of having built a solid solution to the problem as a group and incorporated everyones oppinion into the final solution, we have built something great together.

GO TEAM!


  public void CreateEntity(GameBlueprint blueprint)

{ if (blueprint != null) { if (blueprint.Template is GameObject prefab) { var instance = Object.Instantiate(prefab); if (instance.TryGetComponent(out GameEntity entity)) { if (entity.transform.parent.TryGetComponent(out GameEntity parent)) { _system.AddEntityDependency(parent, entity); } _system.NotifyEntityCreated(entity); } } } }

2024-10-10

Using Roslyn inside Rider

 Recently I was struggling with actually getting the 'roslyn source code generator' to generate things available to my other project inside Rider.

Storing this as a memo for the exact things that made this work so I have a good baseline to branch out from in the future. Might help someone.

Setup the project

- New Solution
-- Generator (class library, netstandard2.0)
-- MyConsoleApp (console app, netstandard2.0)

ManageNuGetPackages

- Microsoft.CodeAnalysis.Analyzers
- Microsoft.CodeAnalysisCSharp.Workspaces
- Microsoft.Compilers.Toolset
- NetStandard.Library 2.x.x

MyConsoleApp-project

Add a dependency to Generator, and then modify the .csproj file like this

<!-- Add this as a new ItemGroup, replacing paths and names appropriately -->
<ItemGroup>
<ProjectReference Include="..\Generator\Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="4.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

Generator-project

Adding launchsettings.json

Create a file at ./Properties/launchSettings.json and paste the following code inside it (I had to create the Properties-Directory inside Rider)

{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Generators": {
"commandName": "DebugRoslynComponent",
"targetProject": "../MyConsoleApp/MyConsoleApp.csproj"
}
} } 

Create the Generator-file

Create a file called HelloWorldGenerator.cs and paste this content inside it
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

namespace Generator;

[Generator]
public class HelloWorldGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{

}

public void Execute(GeneratorExecutionContext context)
{
// begin creating the source we'll inject into the users compilation
var sourceBuilder = new StringBuilder(@"
using System;
namespace HelloWorldGenerated
{
public static class HelloWorld
{
public static void SayHello()
{
Console.WriteLine(""Hello from generated code!"");
Console.WriteLine(""The following syntax trees existed in the compilation that created this program:"");
");

// using the context, get a list of syntax trees in the users compilation
var syntaxTrees = context.Compilation.SyntaxTrees;

// add the filepath of each tree to the class we're building
foreach (SyntaxTree tree in syntaxTrees)
{
sourceBuilder.AppendLine($@"Console.WriteLine(@"" - {tree.FilePath}"");");
}

// finish creating the source to inject
sourceBuilder.Append(@"
}
}
}");

// inject the created source into the users compilation
context.AddSource("helloWorldGenerator", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
}
}
End result 
after doing a couple of attempts at
- Reload project
- Restart Rider
- Rebuild Solution
Inside the console app it should now be possible to write
HelloWorldGenerated.HelloWorld.SayHello();
And inside the 'solution view' you should be able to see the following by expanding
/MyConsoleApp/Dependencies/.NetStandard2.0/Source Generators/Generator.HelloWorldGenerator




2014-10-06

Changing shortcuts in 'Save As'-dialog

I usually restructure my folders on hard drive a bit to keep documents in separate paths depending on project, and usually they don't exist in "my document"-folder. Which makes it a hassle whenever I want to save a document into that folder since the default "save as"-dialog only contains predefined links.

So, today I decided to find out how to work around this, and turns out it is possible, you just need to do some registry magic.

Here's how:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies
Create a new key named 'comdlg32'
Create a new key in that key named 'Placesbar'

Create a new string named 'Place0' and type folder (MyComputer)
Create a new string named 'Place1' and type folder (E:\[Work]\[proj_x])
Create a new string named 'Place2' and type folder (MyDocuments)

(for clarity, this is where all the strings are located: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\comdlg32\Placesbar\...)

Other pre-defined shortcuts of interest:
  • MyDocuments
  • MyPictures
  • MyMusic
  • MyFavorites
  • Desktop
  • ProgramFiles
  • Recent
Victory!

2013-10-23

Setting up a WAMP-server (and how I got through all the issues)

So, I was in need of accessing a file through LAN in order to get my console to download a file from somewhere (and it only accepts URLs). So I thought that would be an easy fix with the help of a web-server through WAMP, since I had used that earlier without any issues.

Turns out some things have changed so here is my walkthrough (long wall of text with tons of steps) of what I experienced and how I solved the issues that arose, in case someone is running into same issue or if I need to do this again:

  1. Download & install latest WAMP-server for Win64 (as of today, that is: (64 BITS & PHP 5.4) 2.4)
  2. Launch wamp-server, LMB on icon and press "Put Online" and test "localhost" in browser (no response)
  3. Turns out port 80 is being used by skype, try to kill skype and try again (make sure it is working)
  4. Change port of wampserver to use 8080
    1. WAMP-icon/LMB/apache/httpd.conf
      1. Change "Listen 80" to "Listen 8080"
      2. Change ServerName-line to "ServerName localhost:8080"
      3. Restart Service
  5. Test again with http://localhost:8080/ (make sure it is working)
  6. Now next part, accessing a folder on server
    1. create a file in wamp-www-folder "c:\tools\wamp\www\temp\foobar.txt"
    2. should be able to navigate to it by typing "localhost:8080/temp/" in browser
  7. And finally, access the file through an IP-address (so you can access it from other device on your network)
    1. WindowsKey/cmd.exe/
      1. ipconfig <enter> (my ip is 10.20.201.75)
      2. Enter that IP into browser such as http://10.20.201.75:8080/temp/
        1. For me this resulted in an error with "you do not have permission to view this folder"
        2. Go into WAMP-icon/LMB/apache/httpd.conf
        3. Change <Directory "c:/tools/wamp/www"> ... </Directory> into:
          <Directory "c:/Tools/Wamp/www">
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Require all granted
          </Directory>
        4. (Basically it seems like all Allow-lines was changed in Apache 2.4 and thus they should be replaced by the Require all granted-line)
        5. At this point all should be working after a restart, but it wasn't for me, the apache-service stopped responding WAMP-icon/LMB/apache/service/"could not start service even after clicking on it several times) and there were no logs in apache_error.log.
          1. Windows-key/cmd/
          2. cd "C:\Tools\wamp\bin\apache\Apache2.4.4\bin\"
          3. httpd -t (will check configs to see if there is an error in a file, turns out I had an error in httpd.conf-file on a specific line which was reported by this command)
          4. Fixed issue in file and restarted service, working as intended
          5. You can also open TaskManager (ctrl+shift+escape)
            1. Services/wampapache and check status, for me it was stopped, which lead me to use the "httpd -t"-command
  8. Good Game - I win (finally able to download a file through console from a http-address)


2013-07-11

Consume CPU / create CPU load

As usual I found myself in need of something and after searching for a solution I stumbled upon something really interesting that I thought might be worth sharing or at least keep there for myself to read next time I'm in need of it.

This time around I wanted to see what happened to an application when it was under super heavy load (100% CPU).

Turns out the easiest solution to achieve that was to run a console command that comes with windows (at least I think it does? It exists on my machine at least; win7, but it might have arrived in some SDK that I have downloaded).

The program is called consume.exe and can be found in c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0a\Bin\x64 (the last part might vary it seems).

What this does is that it gives you the ability to hog system resources for x amount of seconds.

If that doesn't give you what you needed you might find more information in regards to hogging resources on this StackExchange-url.