What are you working on currently? [2016]

been working on an airship for far too long

progress kind of died down, but i hope to finish it with some nice scenery sooner or later

here’s the place with the airship:

8 Likes

That engine is impressive :+1:


Messed around with the idea of 3D animated particle effects with a simple smoke effect. It’s much too pronounced for something like landing from a jump, but it was an easy way to try it out.

17 Likes

thanks, they were a huge pain.

the big engine is based on a sulzer engine from 1914, and has around 1.3k parts.
the two smaller engines are fighter engines from ww1. together they have around 900 parts.

they aren’t accurate at all, but i just keep adding stuff until it’s too complex to make sense of.

Because you said you thought the art was “super cool” , thought you’d be interested lol

Motivation’s started to slip. I’m worried that I’ll just leave this place at 80% like the rest of my builds. Anyway, here’s a sign, now with words!

2 Likes

Was going to put this in its own thread, hence the length, but it’s not worth that, so…

Disclaimer: This is not very useful for reasons that will become clear momentarily. Nevertheless, it’s fun.

So it turns out if you pass error a non-string value, then xpcall the function calling error, the xpcall handler gets the actual value passed to error.

Having learned this, I made exceptions, for…some reason.

Sadly they’re rather limited because xpcall is distinct from ypcall and cannot yield in the invoked function (the handler receives attempt to yield across metamethod/C-call boundary).

Usage

I implemented exceptions as a single module, shoehorning in a try/catch equivalent. You construct an exception using Exception.new, which has the following signature:

Exception Exception.new(string message, string exceptionType = "Exception", Exception inner = nil)

There’s also a shorthand Wrap method that wraps the exception it’s called on in a new exception.

I tossed a try/catch equivalent together too; here’s a rudimentary use:

local Exception = require(script.Parent.Exception)

local function Trim(a)
	if type(a) ~= "string" then
		error(Exception.new("Type mismatch: Got "..typeof(a)..", expected string.", "ArgumentException"))
	end
	
	return a:match("^%s*(.-)%s*$")
end

Exception.Try(Trim, 1, {
	[{ "ArgumentException" }] = function(err)
		print("Caught: "..err.Message, "\n", err.StackTrace)
	end
})

The signature for Exception.Try is a mouthful, but here we go:

void Exception.Try(function tryFunc, Tuple<Variant> args, Dictionary<Array<string>, function<Exception>>)

It takes a function to invoke as the “try” function, a variable number of arguments, and a table of handlers keyed by an array of exception types to handle. It’s ugly, I know.

Source of the exception module:

local Exception = {}
Exception.__index = Exception
Exception.Type = "Exception"

function Exception.new(message, exceptionType, inner)
	return setmetatable({
		Message = message;
		Inner = inner;
		Type = exceptionType;
		StackTrace = debug.traceback();
	}, Exception)
end

function Exception.Try(func, ...)
	local args = { ... }
	local handlers = args[#args]
	table.remove(args, #args)
	
	local success, details = xpcall(function() func(unpack(args)) end, function(err)
		if type(err) ~= "table" then
			return Exception.new("Lua error: "..tostring(err))
		end
		
		for types, handler in pairs(handlers) do
			for _, exceptionType in ipairs(types) do
				if exceptionType == err.Type then
					handler(err)
					return { true, err }
				end
			end
		end
		
		return { false, err }
	end)
	
	if not success and not details[1] then
		-- Toss the exception upwards.
		error(details[2])
	end
end

function Exception:Wrap(message, exceptionType)
	return Exception.new(message, exceptionType, self)
end

function Exception:__tostring()
	local message = {
		self.Type,
		" - ",
		self.Message,
	}
	
	if self.Inner ~= nil then
		table.insert(message, "\n")
		table.insert(message, tostring(self.Inner))
	end
	
	return table.concat(message, "")
end

return Exception

Performance and Notes

Good news! Exceptions are fast in the best case scenario - that is, nothing errors. No exception is thrown. This is because exceptions are an extension built on error and xpcall - if error is never called, xpcall never invokes the handler function, so no (comparatively) slow error processing Lua code gets run.

If an exception is thrown, however, performance can rapidly degrade. The example code above averages 0.65ms to catch the thrown ArgumentException; I’ve not done larger-scale tests with this to see how much of it is constant factors and how much is the two loops through the handlers dictionary.

This is kind of a messy implementation; I’m not likely to clean it up because of the yielding issue (also because this is complete and total overkill for most cases - pcall and string error calls are probably all you need, if that).

I tried using pcall to allow exceptions to yield; pcall doesn’t like the error being a non-string. The second return value is the rather unhelpful string An error occurred instead of anything I can work with. I could error with a JSON encoding of the exception and decode it in the try/catch function but that’s reaching extreme levels of misdirection :stuck_out_tongue:

1 Like

Heres an update on this.

This was the first time I was able to manually compile and setup the materials for an R15 character, using the resources my program was able to export for me. All I had to do was convert the textures over to the VTF format, and I had to compile the character into the .MDL format

I was able to fetch the R15 character texture from my 3D thumbnail on the website. There were specific regions on the character’s texture map, that I was able to split up into individual textures. As for the hats, I just fetched their textures from the meshes themselves. I still don’t have this working for R6, and I’m afraid I might have to generate the R6 texture maps myself, which will be M I L D L Y _ P A I N F U L, but we’ll see how that goes.

Theres still a lot of backend stuff I have to do to completely automate the compilation, and I still need to make the UI for the program. I’m expecting to be done before the end of the month though!

5 Likes

If you need someone to make a gui In C# let me know :slight_smile:

1 Like

Just don’t use xpcall and use pcall.
You can’t pass the literal value, but you could pass a unique string that can be pointed to the exception.
(and to make it look good, have it be the error message, followed by random invisible characters)

Also, in Exception.Try, you should put the handler table before the tuple.
Now you’re blocking varargs and functions that return multiple values.
(you also drop trailing nils, but apparently I’m the only person on ROBLOX that cares about that)

still nice though. I like anything (semi-)haxy in Lua

I could pass an ID but then it requires storing state, and that’s…more effort than I want to go to (and also doesn’t fulfill the point of that implementation, which was to do something with xpcall and error calls with non-string values). Try swallows all return values of the trying function as well as the handlers; that’s somewhat intentional.

Storing state is just dropping it in a table and reading it if the Try thing requests it.

I wonder why pcall just does “An error occured” while xpcall doesn’t.
(granted, xpcall just does return handler(errorMessage), but still, weird pcall needs a string)

You also have to deal with garbage collection; a weak-key table won’t work because strings are interned, so there’s always a reference to them somewhere (I’m not sure whether the intern references prevent garbage collection, though - they might not). Weak-value won’t work either because there are valid cases where an exception has no references to it (right when it’s being thrown for example), so you’d randomly lose exceptions (and wouldn’t that be hard to debug :s) when garbage collection happens. If you use a strong table you get a memory leak because exceptions won’t ever be garbage collected even after they’re thrown / caught.

Theoretically try/catch could free the references, but that introduces a hidden dependency on Try that would be…annoying (“use this function or you will eventually crash your server via a memory leak!”).

Yeah, I’m not sure what’s up with that - I would have expected table: [some memory address] or something, not An error occurred (unless it’s somewhere in pcall itself that’s throwing An error occurred).

Yeah, I wasn’t entirely sure either if using strings as weak keys would work.
I assumed that, as long as the error message is somewhere (e.g. inside the Try) it won’t get collected.

I’ll do a quick test now I guess.
EDIT: Can’t get strings (inside a weak table) to GC

I always thought GC happened between ticks?

You could probably do something haxy, storing the data in a (regular) table, which is stored in a weak table with the coroutine that called Try as (weak) key. But that’s a bit… ill adviced…

EDIT: While writing the above, I thought of a small issue, but completely forgot to write it down:
pcall won’t allow you to get the error stacktrace, as debug.traceback() will run in another stack.
now if xpcall(coroutine.wrap(func)) worked… (which doesn’t work, tried that a long time ago already)

Yeah; I suppose if you have a poorly-placed yielding operation you could get a garbage collection between error creation and throwing. Dunno. It’d take some work.

On another note…soontm
blob.png

6 Likes

the mechanicals on that are absolutely dope

Had some fun building an underwater scene :smile:

32 Likes

Where’s the sea anemone? :fish:

Did you make any of that in Blender, or is it all unions?

Working on a tool for League of Legends :slight_smile: