Logo with title ".Blog"

Briefs

Low-effort content for certain microblogging platforms.

ProximityPrompts make for great general proximity detectors.

The proximity signals are client-only, so validation is still required. But it’s a fast and simple solution for detection. I like to think that it’s been optimized for large numbers of prompts spread across the workspace, so it’d be a winner in that case.

Author’s Note: The tweet this post was derived from was blessed by The Algorithm for some reason.

Using :Once() on a RemoteEvent will cause the first queued event to be received and all other queued events to be discarded.

This is a design flaw: connecting to a signal must not fire the signal, which remotes do. To avoid losing events, the workaround is to enforce a remote to have exactly one consumer. By this logic, :Once() isn’t allowed, because it adds the one consumer then immediately removes it.

The problem is that connecting to the signal fires it and drains the queue. You literally cannot connect multiple listeners without one of them missing the queued events. The only option is a wrapper with one listener that dispatches to multiple listeners.

There are several solutions to the problem. The most backward compatible would be to trigger the dequeue on the first connection as usual, but defer it so that other connections in the same frame have a chance to connect.

Random graphs

Random graph generation. Blue vertices have unexplored edges, while green ones are completely explored. Occasionally connects a new vertex to a nearby existing vertex, forming a loop. Rarely creates a long loop by connecting to the most distant vertex.

The graph is represented by a force-directed graph that moves the vertices around to make them easier to visualize. The actual graph is dimensionless, with the vertices having no 2D or 3D position.

Streaming

A problem with streaming on Roblox is that a client can just move the camera anywhere, and the server will happily stream whatever is at that location, even if the player is not meant to be there. Developers need to be able to exclude areas from being streamed to certain clients.

2023-01-16 23:14: As a follow up, it turns out to be possible with the Player.ReplicationFocus property. Setting it to a dummy part gives the server the opportunity to deny streaming in. The position of the dummy part matches the character, except when the character moves to an undesired location.

1-bit LÖVE

Having some fun with LÖVE.

Here’s a view of the chunk buffer demonstrating simplified chunk loading:

The white area is the viewport. Chunks are updated only when the focus leaves the yellow area. Each corner of the blue area determines which chunks are loaded.

The writers

First AI came for the writers

And nothing was said about the writers because they didn’t make any pretty pictures to look at.

Then AI came for the artists

And nothing was said about the writers because they didn’t make any pretty pictures to look at.

Futility

You can’t spell futility without utility! winks double snap-pointing gesture winning smile with single sparkle accompanied by “ting” sound

Copilot

Maintainers Not So Impressed After Influx of Bug Reports Deriving From Copilot Having No Idea What It’s Talking About

It’s current year and we’re still hell-bent on punishing players for not playing our games correctly instead of just shoving them into their own world where they can play however they like.

AI skimming

It seems like AI is really good at appearing correct at a glance. It’s almost like it’s being trained by humans that are just skimming the results.

Unordered RBXL

I’ve been thinking about an implementation of the binary rbxl format where chunks can decode in any order, and empty space is allowed between chunks, which would enable efficient partial modifications to files.

I have reservations about using retweets/boosts/likes/favorites, but Mastodon seems to be less psychotic about them, so I might use them more there.

Maid pattern

Janitor/Maid/etc are different conveniences built on the same core principal: finalization is established near initialization. I continue to assert that maid is a pattern, so the best solution is to either roll your own, or pick whatever best suits your needs.

Here’s my implementation, which represents my current theory on the matter:

https://github.com/Anaminus/roblox-library/tree/master/modules/Maid

As for memory leaks, no library will save you from having the wrong mindset about memory management. Maids and Janitors are just one of the many possible answers to “What am I going to do about this thing I just created once I no longer need it?”

Case convention

I normally do PascalCase for public, and camelCase for private. But since Roblox’s convention for modules is module.camelCase, I match that instead. So the difference becomes unexportedFunction vs module.exportedFunction, and PascalCase just isn’t used for top-level functions.

Dead project

A perfectly correct but dead project is better than a mostly correct but living project only if the project is already completely finished.

Parody

parody? parodeez nutz

The author would like to formally apologize to his friends, family, and followers for having posted this.

Roact

my favorite part of roact is how you can instantly tell when it’s being used by the complete lack of keyboard support and the general feeling of sluggishness

It would run much better with a proper component implementation.

Of course, but it wasn’t done that way. Why not? Maybe it’s because Roact makes it easy to do the wrong thing. Or hard to do the right thing. Or both.

Transpiling

My beef with transpiling is writing in one language and receiving errors in another language. Or worse, having to mentally map one to the other while stepping through a debugger. It’s not worth it if the productivity gained during writing is lost during debugging.

Module packages

I feel like a problem with modules is that a module’s namespace can’t be divided further than one ModuleScript. It would be nice if there were some kind of Package object, where all child ModuleScripts shared the same environment.

Rewriting Binstruct to be more ergonomic. Also added support for recursive type definitions, which is implemented in the VM as subroutines. Continues to be my favorite module to write.

Rewrote Binstruct’s union type to work like an if statement. Here’s what parsing a value for serialized attributes looks like.

Because type definitions in Binstruct are just tables, it’s really easy to create abstractions to make defining types more ergonomic. This is an example of a builder that allows advanced functions to be added to a definition more easily.

The builder in action. The decode/encode functions convert between a finalized value and an intermediate representation that is more easily digested by the codec, though this representation is often useful enough on its own.

All good data formats have a version number. Here’s a constructor that applies versioning to any type. It uses a union to match a version to one of a number of types. The version is also available as a “global”, allowing the value to be inspected from anywhere in the structure.

Type smuggling

Type smuggling:

type T = typeof(require(...))

Allows you to get types from a module without requiring it. Does not work with exported types.

To get around exporting, types can be smuggled through the returned table.

local export = {}

-- Innocent module stuff.
function export.new()
end

-- Some types. Must require
-- the module in order to get.
export type Foo = string | number
export type Bar = () -> boolean

-- Smuggle them through
-- the returned table.
export._Foo = (nil::any)::Foo
export._Bar = (nil::any)::Bar

return export

This is incredibly useful for smuggling types from a server module in a client module so the data the client is going to receive from the server via a remote can be fully typed.

@fewkz, 6:31 AM · Nov 9, 2022

So nothing breaks on the client even though the modules containing the server types aren’t replicated, because types don’t matter at runtime. Interesting approach.