Re: Using D in Games and bindings to c++ libraries
You can safely forget about RakNet by the looks. "While I still get some inquiries, as of as of March 13, 2015 I've decided to stop licensing so I can focus on changing the world through VR." So based upon this, what would you like to do? Scratch or another pre-existing protocol like proto-buffers?
Re: C binding with D function
A fun project might be to use D's reflection to automatically generate any C binding translators to a nice D interface and the Ruby file to access it. I did something similar back in the day for PHP and JavaScript... it would be kinda tricky to do it with best efficiency, but getting it to work just conveniently would be fairly easy.
Re: call stack tracing
On Saturday, 6 August 2016 at 01:44:04 UTC, Adam D. Ruppe wrote: On Saturday, 6 August 2016 at 01:29:37 UTC, Mark "J" Twain wrote: Of course, exceptions have TraceInfo... could that be used anywhere or hacked to get what I need? The function is defaultTraceHandler in druntime for what the exceptions do: http://arsdnet.net/dcode/stacktrace.d more text about this trick is available in my book btw https://www.packtpub.com/application-development/d-cookbook Thanks. When I try this in windows I get First-chance exception: core.demangle.Demangle.ParseException I had the GC disabled. Enabling it got me the correct results.
Re: call stack tracing
On Saturday, 6 August 2016 at 01:29:37 UTC, Mark "J" Twain wrote: Of course, exceptions have TraceInfo... could that be used anywhere or hacked to get what I need? The function is defaultTraceHandler in druntime for what the exceptions do: http://arsdnet.net/dcode/stacktrace.d more text about this trick is available in my book btw https://www.packtpub.com/application-development/d-cookbook
call stack tracing
Any solution out there that can get call stack, file and line info? Similar to exceptions but I simply want to track memory allocations and need the complete call stack rather than just the previous call(using __FUNCTION__/__FILE__/etc), which is usually useless. Of course, exceptions have TraceInfo... could that be used anywhere or hacked to get what I need?
Re: Get size of mem to free by free
On 08/05/2016 03:49 PM, H.Loom wrote: On Friday, 5 August 2016 at 21:25:41 UTC, Ali Çehreli wrote: malloc stores the allocation size and other information right before the pointer that it returns. TIL. Is it guaranteed for any malloc implementation ? I'm a bit surpised to read that. I thought these kind of trick was only used for aligned allocations ! Not guaranteed of course but it's a common technique. Here is an implementation: https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#1116 As I had suspected, the imprecision in my test code comes from low order bits being used as flags (P and M in that source code). And I think malloc always returns memory properly aligned. After all, the user should be able to put anything there. Ali
Re: Get size of mem to free by free
On Friday, 5 August 2016 at 21:25:41 UTC, Ali Çehreli wrote: malloc stores the allocation size and other information right before the pointer that it returns. TIL. Is it guaranteed for any malloc implementation ? I'm a bit surpised to read that. I thought these kind of trick was only used for aligned allocations !
Re: Get size of mem to free by free
On 08/05/2016 12:55 PM, Mark J Twain wrote: I use malloc to allocate some memory, then free it later. For monitoring purposes, I would like to know how much is free'ed by free by just knowing the object. Or, rather, given a ptr allocated by malloc, bet the block size it allocated from the ptr alone. Some C compilers have special intrinsics and such for this, does D have any ability? If not, any hacks? I just need something that works. malloc stores the allocation size and other information right before the pointer that it returns. You can do better than I do below if you know the exact objects that it stores there. And of course you don't need a struct. I just liked the property idea there. :) import std.stdio; import core.stdc.stdlib; struct AllocatedMemory { void *p; alias SizeType = size_t;// perhaps ulong? @property SizeType size() { return *cast(SizeType*)(p - SizeType.sizeof); } } void myFree(void * p) { auto a = AllocatedMemory(p); writefln("Freeing something like 0x%x bytes", a.size); } void main() { auto p = malloc(0x12345); myFree(p); } Prints: Freeing something like 0x12351 bytes Ali
Re: Get size of mem to free by free
On Friday, 5 August 2016 at 21:07:02 UTC, H.Loom wrote: On Friday, 5 August 2016 at 20:54:59 UTC, Mark "J" Twain wrote: On Friday, 5 August 2016 at 20:43:12 UTC, H.Loom wrote: On Friday, 5 August 2016 at 19:55:22 UTC, Mark "J" Twain wrote: [...] You can wrap the C memory allocations functions with a version identifier, e.g version(stat) { __gshared size_t[size_t] sizes; } version(stat) { auto malloc(size_t size) { auto result = std.c.stdlib.malloc(size); sizes[result] = size; return result; } } else alias malloc = std.c.stdlib.malloc; version(stat) { void free(void* ptr) { std.c.stdlib.free(ptr); sizes.remove(ptr); } } else alias free = std.c.stdlib.free; that a bit a DIY but this would work. Yeah, I ended up just storing the sizes in the ptr on malloc. Essentially the same but I don't have to keep a global around and deal with that. you cannot get rid of a global variable to store the sizes. Even if it's hidden in an API you didn't write, in which case it's just hidden. Have you looked at experimental allocators ? More especially https://github.com/dlang/phobos/blob/master/std/experimental/allocator/building_blocks/stats_collector.d instead of using malloc/free you can use Mallocator (this is the same). Maybe that StatsCollector!Mallocator == win for you.
Re: Get size of mem to free by free
On Friday, 5 August 2016 at 20:54:59 UTC, Mark "J" Twain wrote: On Friday, 5 August 2016 at 20:43:12 UTC, H.Loom wrote: On Friday, 5 August 2016 at 19:55:22 UTC, Mark "J" Twain wrote: [...] You can wrap the C memory allocations functions with a version identifier, e.g version(stat) { __gshared size_t[size_t] sizes; } version(stat) { auto malloc(size_t size) { auto result = std.c.stdlib.malloc(size); sizes[result] = size; return result; } } else alias malloc = std.c.stdlib.malloc; version(stat) { void free(void* ptr) { std.c.stdlib.free(ptr); sizes.remove(ptr); } } else alias free = std.c.stdlib.free; that a bit a DIY but this would work. Yeah, I ended up just storing the sizes in the ptr on malloc. Essentially the same but I don't have to keep a global around and deal with that. you cannot get rid of a global variable to store the sizes. Even if it's hidden in an API you didn't write, in which case it's just hidden. Have you looked at experimental allocators ?
Using D in Games and bindings to c++ libraries
Hi, I have some experience with c++, and am considering using either D or python for a game. 1. It is going to be online-based, would D be a good choice for writing both the client and server? I haven't seen any online games using D yet. 2. A concern is interfacing with c++. I'll like to use the RakNet networking library, but there aren't any bindings for it. a) Are there instructions available for using Calypso on windows? Couldn't find any, and it looks like a good alternative to writing one by hand. b) How well would SWIG work? I haven't used multiple programming languages in a project before, so have no experience doing this sort of thing.
Re: Get size of mem to free by free
On Friday, 5 August 2016 at 20:43:12 UTC, H.Loom wrote: On Friday, 5 August 2016 at 19:55:22 UTC, Mark "J" Twain wrote: [...] You can wrap the C memory allocations functions with a version identifier, e.g version(stat) { __gshared size_t[size_t] sizes; } version(stat) { auto malloc(size_t size) { auto result = std.c.stdlib.malloc; sizes[result] = size; return result; } } else alias malloc = std.c.stdlib.malloc; version(stat) { void free(void* ptr) { std.c.stdlib.free(ptr); sizes.remove(ptr); } } else alias free = std.c.stdlib.free; that a bit a DIY but this would work. Yeah, I ended up just storing the sizes in the ptr on malloc. Essentially the same but I don't have to keep a global around and deal with that.
Re: Get size of mem to free by free
On Friday, 5 August 2016 at 19:55:22 UTC, Mark "J" Twain wrote: I use malloc to allocate some memory, then free it later. For monitoring purposes, I would like to know how much is free'ed by free by just knowing the object. Or, rather, given a ptr allocated by malloc, bet the block size it allocated from the ptr alone. Some C compilers have special intrinsics and such for this, does D have any ability? If not, any hacks? I just need something that works. You can wrap the C memory allocations functions with a version identifier, e.g version(stat) { __gshared size_t[size_t] sizes; } version(stat) { auto malloc(size_t size) { auto result = std.c.stdlib.malloc; sizes[result] = size; return result; } } else alias malloc = std.c.stdlib.malloc; version(stat) { void free(void* ptr) { std.c.stdlib.free(ptr); sizes.remove(ptr); } } else alias free = std.c.stdlib.free; that a bit a DIY but this would work.
Re: vsprintf or printf variable arguments
On Friday, 5 August 2016 at 19:21:38 UTC, Mark "J" Twain wrote: On Friday, 5 August 2016 at 08:32:42 UTC, kink wrote: On Thursday, 4 August 2016 at 21:03:52 UTC, Mark "J" Twain [...] This has absolutely nothing to do with D as these are C functions, so you'd be better off asking this in another forum. Um, then I wonder why I am using D? Why does D even have C stuff in it if it has ABSOLUTELY nothing to do with D? Anyway, I have no idea why you'd want to construct the va_list manually. Of course you don't, do you have ESP? Do you need to see a psychologist? These vprintf() functions only exist so that other variadic functions can forward THEIR varargs - e.g., extern(C) void myOldschoolPrintf(in char* format, ...) { doSomethingSpecial(); va_list myVarargs = va_start(format); vprintf(format, myVarargs); } Note that va_list is highly platform-dependent, so filling the struct manually is a very bad idea. So? People do bad stuff all the time, you should know a lot about that? Or are you Jesus? Ok, thanks for the help. I appreciate it! I know absolutely nothing more than I did about the problem I asked. At least my reply to my self works and accomplishes the task I posted about in the first place. Could you please stop changing your alias from; Joerg Joergonson to Hiemlick Hiemlicker to Adam Sansier to Rufus Smith to Mark "J" Twain so that those who don't want to read you can filter you out once and for all. Just pick one and stick with it! Thanks!
Get size of mem to free by free
I use malloc to allocate some memory, then free it later. For monitoring purposes, I would like to know how much is free'ed by free by just knowing the object. Or, rather, given a ptr allocated by malloc, bet the block size it allocated from the ptr alone. Some C compilers have special intrinsics and such for this, does D have any ability? If not, any hacks? I just need something that works.
Re: vsprintf or printf variable arguments
On Friday, 5 August 2016 at 08:32:42 UTC, kink wrote: On Thursday, 4 August 2016 at 21:03:52 UTC, Mark "J" Twain [...] This has absolutely nothing to do with D as these are C functions, so you'd be better off asking this in another forum. Um, then I wonder why I am using D? Why does D even have C stuff in it if it has ABSOLUTELY nothing to do with D? Anyway, I have no idea why you'd want to construct the va_list manually. Of course you don't, do you have ESP? Do you need to see a psychologist? These vprintf() functions only exist so that other variadic functions can forward THEIR varargs - e.g., extern(C) void myOldschoolPrintf(in char* format, ...) { doSomethingSpecial(); va_list myVarargs = va_start(format); vprintf(format, myVarargs); } Note that va_list is highly platform-dependent, so filling the struct manually is a very bad idea. So? People do bad stuff all the time, you should know a lot about that? Or are you Jesus? Ok, thanks for the help. I appreciate it! I know absolutely nothing more than I did about the problem I asked. At least my reply to my self works and accomplishes the task I posted about in the first place.
Re: Linking on MS Windows.
On Friday, 5 August 2016 at 18:37:43 UTC, Jonathan M Davis wrote: I know that dmd supports VS' binary format and linker for 64-bit (dmc and optlink were never updated to support 64-bit), and I think that 32-bit support for using VS' library format and linker were added later. So, I'm fairly certain that you could compile your D program to be compatible with that statically linked library by using the right compiler flag with dmd. But I don't use Windows much aside from work, so I'm not very familiar with how to use dmd with Windows beyond the basics and am not going to be very helpful in telling you how to actually do it. You might be able to figure it out by looking at dmd's compiler flags, but if not, I'm sure that someone else here who actually uses Windows with D will be able to tell you. - Jonathan M Davis Owing to your reply I found this: http://stackoverflow.com/questions/36332219/linking-with-c-libraries-on-windows-with-dub And it even seems to work. Thanks.
Re: Linking on MS Windows.
On Friday, 5 August 2016 at 18:28:48 UTC, ciechowoj wrote: Is default dmd linker (on MS Windows, OPTILINK) supposed to link against static libraries created with Visual Studio? Specifically I want to link a project compiled on windows with dmd against pre-compiled library `libclang.lib` from LLVM suite. I'm pretty sure they used Visual Studio to compile the library. No, because DMD win32 produces OMF objects (.lib are archived collection of objects) while VS will produce collection of COFF in the lib.
Re: Linking on MS Windows.
On Friday, August 05, 2016 18:28:48 ciechowoj via Digitalmars-d-learn wrote: > Is default dmd linker (on MS Windows, OPTILINK) supposed to link > against static libraries created with Visual Studio? > > Specifically I want to link a project compiled on windows with > dmd against pre-compiled library `libclang.lib` from LLVM suite. > I'm pretty sure they used Visual Studio to compile the library. dmc (and thus optlink) and VS use different library formats. So, you can't mix any libraries between them unless you dynamically load them at runtime (which can't be done with a statically linked library). Neither statically linked libraries nor dynamically linked libraries compiled by one compile can be mixed with those from the other. I know that dmd supports VS' binary format and linker for 64-bit (dmc and optlink were never updated to support 64-bit), and I think that 32-bit support for using VS' library format and linker were added later. So, I'm fairly certain that you could compile your D program to be compatible with that statically linked library by using the right compiler flag with dmd. But I don't use Windows much aside from work, so I'm not very familiar with how to use dmd with Windows beyond the basics and am not going to be very helpful in telling you how to actually do it. You might be able to figure it out by looking at dmd's compiler flags, but if not, I'm sure that someone else here who actually uses Windows with D will be able to tell you. - Jonathan M Davis
Linking on MS Windows.
Is default dmd linker (on MS Windows, OPTILINK) supposed to link against static libraries created with Visual Studio? Specifically I want to link a project compiled on windows with dmd against pre-compiled library `libclang.lib` from LLVM suite. I'm pretty sure they used Visual Studio to compile the library.
Re: assert or throw in range members?
On 8/5/16 6:25 AM, Nordlöw wrote: Should range members front() and back() assert() or throw() on emptyness? If it should assert() doesn't that lead to unsafer code in release mode? What's the consensus here? If the code is @safe, then it should trigger an error if you try to do unsafe things, regardless of asserts. If you mark front or back or whatever @safe, and do unsafe things, then you will have to mark it as @trusted. In this case, instead of asserting, use if(cond) assert(0), or use enforce, as you can't allow unsafe behavior in a @trusted function based on the -release switch. -Steve
Re: C binding with D function
On Thursday, 4 August 2016 at 13:14:34 UTC, llaine wrote: Here is my repo https://github.com/llaine/ruby-dlang FYI: to!string is kinda slow. If you want to do a fast blank, you should avoid conversions actually, I'm surprised it is faster than the native Ruby one at all.
Re: Indexing with an arbitrary type
On Thursday, August 04, 2016 08:13:59 Alex via Digitalmars-d-learn wrote: > What I think about is something like this: > https://dpaste.dzfl.pl/d37cfb8e513d Okay, you have enum bool isKey(T) = is(typeof(T.init < T.init) : bool); template isNullable(T) { enum isNullable = __traits(compiles, T.init.isNull); } struct IndexAble { int[] arr; this(size_t size) { arr.length = size; } ref int opIndex(T)(T ind) if(isKey!T) { static if(isNullable!T) if(ind.isNull) return arr[$]; // or another manually defined state. return arr[ind]; } } What happens if I do this? void main() { IndexAble indexable; indexable["hello"]; } You get a compilation error like q.d(17): Error: cannot implicitly convert expression (ind) of type string to ulong q.d(24): Error: template instance q.IndexAble.opIndex!string error instantiating because the index type of int[] is size_t, so it's only going to accept values that are implicitly convertible to size_t, and string is not implicitly convertible to size_t. opIndex's template constraint did not catch the fact that passing "hello" to opIndex would result in a compilation error. string is perfectly comparable, and it's not a valid index type for int[] - and comparability is all that opIndex's template constraint checks for. For opIndex to actually catch that an argument is not going to compile if opIndex is instantiated with that type, then its template constraint needs to either test that the index type will implicitly convert to size_t so that it can be used to index the int[], or it needs to test that indexing int[] with the argument will compile. So, something like ref int opIndex(T)(T ind) if(is(T : size_t)) {...} or ref int opIndex(T)(T ind) if(__traits(compiles, arr[ind])) {...} And given that you're dealing explicitly with int[] and not an arbitrary type, it really makes more sense to use is(T : size_t). If IndexAble were templated on the type for arr, then you would need to do the second. But testing for comparability is completely insufficient for testing whether the type can be used as an index. In fact, it's pretty much irrelevant. What matters is whether the index type will compile with the code that it's being used in inside the template. If you're forwarding the index variable to another object's opIndex, then what matters is whether that opIndex will compile with the index, and if you're doing something else with it, then what matters is that it compiles with whatever it is that you're doing with it. I could declare a hash map implementation which did not use opCmp but just used opEquals and toHash. Then I could have a struct like struct S { int i; int j; size_t toHash() { return i * j % size_t.max; } } It has an opEquals (the implicit one declared by the compiler). It has a toHash. It does not have opCmp (and the compiler won't generate one). So, it's not comparable. But it would work in my hash map type only used toHash and opEquals and not opCmp. HashMap!(S, string) hm; hm[S(5, 22)] = "hello world"; And actually, while D's built-in AA's used to use opCmp (and thus required it), they don't anymore. So, that code even works with the built-in AA's. e.g. string[S] aa; aa[S(5, 22)] = "hello world"; So, comparability has nothing to do with whether a particular type will work as the key for an indexable type. What matters is that the index operator be given an argument that will compile with it - which usually means a type which is implicitly convertible to the type that it uses for its indices but in more complex implementations where opIndex might accept a variety of types for whatever reason, then what matters is whether the type that you want to pass it will compile with it. And unless all that that particular opIndex implentation needs to care about for a type to work with it is that the type is comparable, then testing that the type that you're going to give it is comparable is insufficient and may not even be relevant. - Jonathan M Davis
Re: Pass Tid[string] to another thread (by send)
On Friday, 5 August 2016 at 13:14:41 UTC, Kagamin wrote: Bug 6585 is not fixed. Variant wasn't written for shared and immutable types. As a workaround wrap the array in a class. Thanks! It worked. Though it's strange that Variant wasn't written for shared and immutable types because, for instance, in TDPL it has been used in multithreading context.
Improper display of Cyrillic in `pragma (msg, ...)` on Windows
Hi, To view Cyrillic CMD on Windows can be used `std.process.executeShell("chcp 65001 ");` and it works. What should I use to change the encoding to UTF-8 to the compiler messages in `pragma(msg, ...)` on Visual D? // import std.stdio, std.process; void main() { executeShell("chcp 65001"); writeln("Привет, мир!"); // Привет, мир! -> OK pragma(msg, "Привет, мир!"); // Привет, РјРёСЂ! -> wrong } // PS C++ supports the following directive in Visual Studio: https://msdn.microsoft.com/en-us/library/mt708823.aspx
Re: assert or throw in range members?
On Friday, August 05, 2016 15:50:50 H.Loom via Digitalmars-d-learn wrote: > On Friday, 5 August 2016 at 10:25:42 UTC, Nordlöw wrote: > > Should range members front() and back() assert() or throw() on > > emptyness? > > > > If it should assert() doesn't that lead to unsafer code in > > release mode? > > > > What's the consensus here? > > nothing, but examples exist in pbobos such as front() on narrow > strings. That's because of invalid Unicode, not because the string was empty. It asserts that the string is non-empty. No code anywhere should ever be calling front or back on an empty range, and it is a programming error if it does, not a recoverable error. We obviously don't have control over what everyone else does with their code, but Phobos does not throw an exception in such cases - it asserts if it does anything - and it is definitely the consensus of the main devs that you're not using the range API correctly if code ever calls front or back on an empty range. It is a bug to do so. There were even some recent updates to the documentation to make some of the basic rules around the range primitives clearer, though since it was in the documenation for std.range, it won't be up until the next release: https://github.com/dlang/phobos/pull/4511 - Jonathan M Davis
Re: assert or throw in range members?
On Friday, 5 August 2016 at 10:25:42 UTC, Nordlöw wrote: Should range members front() and back() assert() or throw() on emptyness? If it should assert() doesn't that lead to unsafer code in release mode? What's the consensus here? nothing, but examples exist in pbobos such as front() on narrow strings.
Re: Pass Tid[string] to another thread (by send)
Bug 6585 is not fixed. Variant wasn't written for shared and immutable types. As a workaround wrap the array in a class.
Re: assert or throw in range members?
On Friday, 5 August 2016 at 10:25:42 UTC, Nordlöw wrote: If it should assert() doesn't that lead to unsafer code in release mode? What's the consensus here? It is unsafer, the rationale is that your code should be debugged to comply with the assumption of the release mode that there are no bugs. The consensus is that it's ok that the assumption is backed by a hope (there were bugs that would be caught by asserts and bound checks, but weren't).
Re: assert or throw in range members?
On Friday, August 05, 2016 10:25:42 Nordlöw via Digitalmars-d-learn wrote: > Should range members front() and back() assert() or throw() on > emptyness? > > If it should assert() doesn't that lead to unsafer code in > release mode? > > What's the consensus here? It is a programming error for any range members other than empty, length, or save to be called when a range is empty. So, it definitely should not be an exception. Phobos asserts on such things, though if you're paranoid about it, you can always explicitly throw an Error of some kind (e.g. RangeError). But that will incur a performance hit in release mode, and it's perfectly normal for checks like this to not be enabled in release mode. Testing is supposed to catch such problems before you actually release your code. Regardless, throwing an Exception is the wrong behavior, because it's considered a programming error for those functions to be called when a range is empty. Nothing should ever result in front or back being called when the range is empty. - Jonathan M Davis
Re: assert or throw in range members?
On Friday, 5 August 2016 at 10:25:42 UTC, Nordlöw wrote: Should range members front() and back() assert() or throw() on emptyness? If it should assert() doesn't that lead to unsafer code in release mode? What's the consensus here? if you can and it is not costly, do `assert(false, "BOOM!");` there. as it is UB anyway, you can make programmer's life little easier. ;-) also, `assert()` doesn't break @nogc, if somebody cares.
Re: assert or throw in range members?
On 08/05/2016 12:25 PM, Nordlöw wrote: Should range members front() and back() assert() or throw() on emptyness? If it should assert() doesn't that lead to unsafer code in release mode? What's the consensus here? It's an error in the program when front/back is called on an empty range. Every program must be written so that this cannot ever happen. assert is for catching programming errors. So assert is ok here. Not checking at all would be ok, too, but assert is a bit nicer to the user of the range, of course.
Re: assert or throw in range members?
On Friday, 5 August 2016 at 10:25:42 UTC, Nordlöw wrote: Should range members front() and back() assert() or throw() on emptyness? I'm pretty sure it's assert() here. The contract is that the caller is responsible for checking emptiness beforehand, and the whole of Phobos is coded around that contract. I think. If it should assert() doesn't that lead to unsafer code in release mode? That's the point of release mode. Omitting superfluous checks based on the assumption that your code is correct (e.g. assumption that the emptiness contract is respected).
assert or throw in range members?
Should range members front() and back() assert() or throw() on emptyness? If it should assert() doesn't that lead to unsafer code in release mode? What's the consensus here?
Re: vsprintf or printf variable arguments
On Thursday, 4 August 2016 at 21:03:52 UTC, Mark "J" Twain wrote: How can I construct a va_list for vsprintf when all I have is the a list of pointers to the data, without their type info? A va_list seems to be a packed struct of values and/or pointers to the data. While I could construct such a list, theoretically, I don't always know when I should store an element as a pointer or a value. e.g., ints, floats, and other value types seems to get stored directly in the list, while strings and *other* stuff get stored as pointers. I would be nice if a printf variant listed takes only a pointer list to all the values, does anything like this exist? If not, is there any standardization of what is a value in the list and what is a pointer so I can attempt to build the list properly? This has absolutely nothing to do with D as these are C functions, so you'd be better off asking this in another forum. Anyway, I have no idea why you'd want to construct the va_list manually. These vprintf() functions only exist so that other variadic functions can forward THEIR varargs - e.g., extern(C) void myOldschoolPrintf(in char* format, ...) { doSomethingSpecial(); va_list myVarargs = va_start(format); vprintf(format, myVarargs); } Note that va_list is highly platform-dependent, so filling the struct manually is a very bad idea.
Re: C binding with D function
On Thursday, 4 August 2016 at 23:35:55 UTC, bachmeier wrote: On Thursday, 4 August 2016 at 17:11:04 UTC, Adam D. Ruppe wrote: Happy that you guys put this on the Wiki !!