What's everybody working on? (September Edition)

MMO Update!

I wasn’t going to post another update, because I thought I hadn’t really changed much since then. However, when I reviewed my previous post… I’m up to 17kloc and the ecs switch netted me negative lines.
The changes off the top of my head

  • My amazing <3 and busy wife, who happens to be a graphics and UX designer, made me a sidebar gui.
  • implemented a quest system. integrated with the dialog
  • Moved my networking/serialization, ecs, and String Itern Pool their own public repos. I plan to continue to break off more pieces as I go!
  • Fully rewrote all my zon parse to make it way easier to work with. Now the data that I read in doesn’t need 2 definitions. Previously there were a lot of RawItem → BakedItem for processing zon data into a more efficient formats in memory.
    • Thanks to that I could get rid of all my std.zon.parser patches, Except permitting parsing void fields. I have several fields define as below because it’s a client/server monorepo.
entity_description: if (Role == .Client) []const u8 else void,
  • Added combat, Stats, Equipment, render some eqiupment(I haven’t checked pants and don’t want to).
  • Properly added animations to models
  • Made most of my text translatable
  • Massively improved my in Godot tooling and setup f16 heightmap → Terrain3d importer
  • fixed all memory leaks(painless thanks to the debug allocator)
  • Learned about kcov and started playing number-go-up and I think I’m at about 80% coverage on client and server
  • Properly more, but thats what I can find in my poorly writen commit logs

=== My zon solution ===
I really took a liking to how DVUI makes unit’ed Point types with pub fn Point(comptime unit: UnitEnum) type and have been expanding on the pattern. I create an enum(Pantry) that describes which environment the type is in(zon or baked)

pub const Pantry = enum(u8) {
    zon,
    baked,
    pub fn ClientStr(comptime s: @This()) type {
        if (common.Role != .Client) return void;
        return switch (s) {
            .zon => zon.Str,
            .baked => common.StrRef,
        };
    }
    pub fn Reward(comptime s: @This()) type {
        return zon.Reward(s);
    }
    pub fn ItemStack(comptime s: @This()) type {
        return switch (s) {
            .zon => zon.ItemStack,
            .baked => common.ItemStack,
        };
    }
    pub fn ItemTag(comptime s: @This()) type {
        return switch (s) {
            .zon => zon.Str,
            .baked => common.Tag,
        };
    }
...
}

Sometime I get lucky and the data doesn’t require structural changes. Then, I can just create a function to generate the baked version! I gain a nesting level, but save on the line maintenance.

pub fn Reward(comptime p: Pantry) type {
    return struct {
        items: []p.ItemStack() = &.{},
        exp: []p.SkillExp() = &.{},
        pub fn bake(zonself: Reward(.zon), alloc: Alloc) Alloc.Error!Reward(.baked) {
            const items = try ItemStack.bake_list(zonself.items, alloc);
            const exp = try alloc.alloc(SkillExp(.baked), zonself.exp.len);
            for (zonself.exp, exp) |z, *e| e.* = z.bake();
            return .{
                .items = items,
                .exp = exp,
            };
        }
    };
}
13 Likes

The game is trodding along. Did more playtesting and the game part is kinda done for now. Better progression, more strategic depth and the UI also got a big improvement. Even has a little AI (that does nothing more than colonize & attack) to test stuff out. But the whole HTML/account/game queue/… stuff is lacking, which I successfully procrastinate. After my girlfriend provided her voice to get some strongly modulated roboty advisor lines “ENEMY … DESTROYED”, I thought some self-made music would be cool, too. What a nice opportunity to learn about digitial synths.

And it’s so easy to get something running! Spit out 440hz sine wave with 48khz f32 samples, and you can get the sound just with zig build run | pw-play --raw --format=f32 --rate=48000 --channels=1 -. Using zig instead of a GUI is also viable, with the incremental compilation you can get quick feedback on change, too. Looks like this:

const drum = drum: {
    const mel = &melody6_player;
    var note_freq: EventFrequency = .init(&mel.mod);

    var vol_adsr: ADSR = .init(.{ .events = &mel.mod, .attack = 0.001, .decay = 0.5, .sustain = 0.0, .release = 0.5 });
    var freq_adsr: ADSR = .init(.{ .events = &mel.mod, .attack = 0.001, .decay = 0.01, .sustain = 0, .release = 0.1 });

    var noise: WhiteNoise = .init(.{});
    var f1: MultiplyAdd = .init(&freq_adsr.mod, .{ .fixed = 2 }, .{ .fixed = 0 });
    var lpf: LowPassFilter = .init(.{ .input = &noise.mod, .cutoffs_base = &note_freq.mod, .cutoffs_extra = &f1.mod });
    var lpf2: LowPassFilter = .init(.{ .input = &lpf.mod, .cutoffs_base = &note_freq.mod, .cutoffs_extra = &f1.mod });
    var volume: MultiplyAdd = .init(&lpf2.mod, .{ .mod = &vol_adsr.mod }, .neutral);
    var gain: MultiplyAdd = .init(&volume.mod, .{ .fixed = 8.0 }, .neutral);
    var reverber: Reverber = try .init(&gain.mod, gpa, rnd, .{
        .delay = .{ .time = 0.5 },
        .min = 0.01,
        .feedback = 0.9,
        .lines = 32,
        .sample_rate = rate,
        .wet = 0.25,
        .gain = 0.6,
        .low_pass_freq = 110,
        .high_pass_freq = 110,
        .feedback_pitch_shift = -4.0,
    });

    break :drum &reverber.mod;
};

And with a melody DLS copied from strudel (copied from TidalCycles)

\\4 _ 0 [_ 2]     4 _     [_ 2] 0
\\4 _ 0 [_ _ 2 2] 4 [0 0] 2     _

It sounds like this: https://λ.land/drums.mp3
Also tried some spacy star sparkle sound: https://λ.land/sparkle.mp3
And ambient space wobbles: https://λ.land/space.mp3

Most time was spent on implementing a nice sounding reverb with a pitch shift (and even more time to even find details on how to do it).

13 Likes

There already is a project very similar to anyzig (though, not a fork) which supports mirrors. I switched to it a few days ago from anyzig and iguana and I’m very happy: https://codeberg.org/TemariVirus/zxc (I’m not the maintainer)

Would really love to hear more about this. Maybe we could merge this with already existing plans for kind of zig.nrw meeting.

Beside this comments I’m still working on my tui file manager lui and updated my simple CLI arcs parser/lexer bib lexopts to 0.17

3 Likes

I sadly had not so much time last month, but I still achieved the most important things I wanted to tackle:

  • Code refactoring
  • Used (very simple) comptime to make it possible to use different sizes of vectors for my SIMD functions
  • Created a custom file format to save/load chunks
  • Basic Input handling to create different buildings / roads (without UI yet)

I really enjoyed using Zigs packed structs to create a custom file format. Because the map is huge I optimized for space and not for save/load performance.
That said I’m still very happy with the performance and even more with the memory usage. To be honest I have never been happier with any of my file formats and saving / loading performance.

Overall I can say that I was never more certain how much memory my program consumes because everything is so explicit and clear.
I repeat myself, but I really enjoy this aspect of working with Zig.
It makes it very easy to set bounds for certain things, like the maximum number of chunks I want to keep in memory, instead of relying on guesstimations or having to check it during runtime.

It’s not much to show but I am extremely happy with the results I achieved this month.

Buildings

Buildings with roads
Roads are just lines. Maybe I will change it so that roads also are tiles/images.

When zoomed out the buildings become red squares.
It has to be changed/tuned so that small amounts of buildings do not get lost when zooming out very far.

And a video showing the placement of the buildings and roads

I learned all the Zig things I set out to learn with this first project. I haven’t decided yet wheter or not I want to continue with this project just for fun.
I still have unfinished things from my original TODO List, so maybe I will at least complete those before archiving this as a learning experience :slight_smile:

The month of August also marks my first contribution to an open source Zig project. It was only the tiniest change to fix a build problem.
But it still feels good to not only use the work of others but also to give back something.
And for me it also was a big step to feel confident enough to make a contribution to a Zig project after 3 month of using the language. :slight_smile:

It is really awesome to see what everyone is working on. I really enjoy this format.

12 Likes

Still working on my Hush browser.

Highlights from the month:

  • Added support for a few more html tags
  • Fixed a few URI bugs
  • Added a janky command pallet and downloading all images in a page
8 Likes

Working on adding C bitfields support to translate-c. Explored some naive ways of achieving this but I’m recently more leaning towards converting adjacent bitfields into a [N]u8 where N is determined based on the total bit width + the alignment.

Eg.

struct Test {
    int a : 16;
    int b : 7;
    int c;
};

int main() {
    struct Test t;

    t.a = 10;
    t.b = 20;

    func(t.a);

    return 0;
}

roughly turns into:

const Test = extern struct {
    bitfield_1: __bitfield_1_Test,
};

// The length is computed here by basically calculating the total bit size / 8
// let's not think of the alignment for now
const __bitfield_1_Test = extern struct {
    inner: [1]u8,

    // modifier and accessor functions such as setters, getters and operators
};

pub fn main() void {
    var t: Test = undefined;

    t.bitfield_1.set_a(10);
    t.bitfield_1.set_b(20);

    func(t.bitfield_1.a());
}

At start, I considered taking the naive approach and converting adjacent bitfields to packed structs containing uN types but then the backing int in Zig gets problemmatic when you want to represent bitfields that cross the boundaries of different powers-of-two-sized integers. Hence switched to the array option.

This will require a lot of tailoring based on the underlying ABI but I’m mostly concerned with getting to know the codebase better and be able to impl the above conversion. I was able to figure out adding the inner struct and modifying the C accessors etc and getting this now:

pub const struct_Test_bitfield_0 = extern struct {
    // `1` is computed based on the total bit width (probably need to account for the offset
    // of the next field to either extend this array or add additional padding field)
    inner: [1]u8 align(1),
};

pub const struct_Test = extern struct {
    bitfield_0: struct_Test_bitfield_0 align(1),
    c: c_int,
};
pub export fn func(arg_val: c_int) void {
    var val = arg_val;
    _ = &val;
}
pub export fn main() c_int {
    var t: struct_Test = undefined;
    _ = &t;
    t.struct_Test_bitfield_0.a() = 10; // havent looked at the modifiers yet lol
    t.struct_Test_bitfield_0.b() = 20;
    func(t.struct_Test_bitfield_0.a());
    return 0;
}

This might not be the proper/best solution but at least im learning so much about the codebase and that’s still very valuable.

6 Likes

I have followed the Zig community from the sidelines for a while, but have just decided to join you “officially” :slight_smile:

My first project is going to be a Zig port of libgpod, a library for transferring music files from/to old iPods.

To handle this process now, I use iTunes on an old Windows laptop I have laying around, which is not a viable option in the long run, since I am on a Linux system. I have tried doing it via music players such as Strawberry and Rhythmbox (and even running iTunes through Wine), but nothing has worked for me.

Besides the fact that the libgpod repo had its last commit in 2011 (excluding forks), the project also contains ca. 20.000 lines of C. I have a lingering feeling that it does not need to be that complicated :wink: but who knows, I might be humbled.

I hope to one day be able to contribute meaningfully to the language, so I’m looking forward to “digging in” and writing some Zig :smiley:

18 Likes

I’m working on a small fantasy console (vex) just for fun lately.

A few example “carts” built on top of vex.zig (the Zig “SDK” for vex)
(Scaffolded using my vex-init tool)

  • play.c7.se/vex/vex-teapot/ (source under src/cart.zig)
  • play.c7.se/vex/vex-raymarch/
  • play.c7.se/vex/vex-raycaster/
  • play.c7.se/vex/vex-sfx/

And as a small palate cleanser I found a tiny fantasy machine called 4BoD, 4 Bits of Doom, on the Esolang wiki and ended up learning a bit more about how a build.zig can be structured to allow for multiple executables to share modules (a small assembler and a compiler in this case)

13 Likes

Love this book, highly recommend it. It takes a very ground-up approach to learning computers and a little bit of their history.

1 Like

I’ve spent the past month working on an audio decoding package writing decoders from scratch. I was originally working on a music player but got sidetracked getting annoyed at FFmpeg.

So far I’m only decoding FLAC files, but the progress I’ve made on that is really good. Yesterday I got this very awesome decoding test summary (with a collection of test files the IETF working group compiled):

There’s still stuff to fix/improve/add with FLAC (namely I need to implement seeking to different points in a file), but aside from that my next step is moving on to MP3.

6 Likes

Currently working on a midi IO library and I am about to experiment the Microsoft Midi Services to see how their api works. Hopefully, I could find a solution to for creating and connecting UMP endpoints for multiple platforms.

3 Likes

Working on “zopt” (https://codeberg.org/hgrsd/zopt) - a low-ceremony command line argument parser.

It’s meant to be an ergonomic option where you “just need to parse a bunch of flags/options/positionals” but don’t have a complicated hierarchy, subcommands, or other more sophisticated needs. It has a bunch of rough edges still and the schemaless design means some of the “table stakes” functionality (such as determining which arguments are positional as opposed to flags, options, or option values) is tricky to get right.

4 Likes

I’m building a simple GUI application to control the scoreboard overlay for fighting game streams:



This is actually my third go at this (first version written in python + tkinter, then rewritten using Go and tcl/tk, then this). It has become kind of my own “todo app” to try out different approaches to desktop GUIs. I’ve found dvui to be very nice - immediate mode GUI is pretty ergonomic, and they really care about getting the little details right (case in point: text entry widgets).

10 Likes

Did you consider not having a struct for Zopt and instead use the root.zig file structure, allowing for const zopt = @import("zopt"); instead of const Zopt = @import("zopt").Zopt;

Ooh, I did not. I’ll have to look into it, it’s a pattern I’m not familiar with yet. Where does the state live in that case, if not in the struct?

1 Like

The file is the struct :slight_smile:

So you can const Self = @This(); directly in src/root.zig to get a handle to it.

2 Likes

Ah, that part of the Zig module model didn’t really click until now. That’s a very nice idea, thank you.

1 Like

As mentioned above I’m working on my ls-style tui filemanager lui.

I now stepped away from the idea of implementing an internal mode for managing plain text notes. Instead I want to implement some features that are very helpful for notes management, but can also be used with regular dirs/plain text files:

  • filter plain text files recursivley by string pattern matching on the content (wip right now)
  • filter subdirs by pattern matching on the filename/path
  • create new plain text files from template with simple template system
  • maybe: run shell commands on selected file/dir from within lui

Currently I’m getting my head into SIMD-boosted search optimization for string pattern matching. Something I haven’t dealt with before. However, so far my test results are quite promising and maybe I’ll give the implementation a try the upcoming days after figuring out whats the best way to implement this into the tui framework.

Unfortunately, my free holiday time will be over on Monday and that might shift my focus back to S3 stuff due to my job priorities…

3 Likes

I’m still working on implementing the WebRTC stack. Currently I’m adding support for data channels that allows sending arbitrary data between peers.

Next I’d like to build a simple video player GUI that decodes and shows a video received from another peer.

4 Likes

I am working on a spv → wgsl transpiler written in zig so we can use zig for shaders on the web. Also a software/gpu renderer that can run the same vertex/fragment shader zig code on cpu or gpu.

5 Likes