Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - chaos

Pages: [1] 2 3
1
Code: [Select]
// This is a port of http://fossies.org/dox/gsl-1.16/gausszig_8c_source.html, is copyrighted 2005 Jochen Voss and 2014
// Matthew Sheahan, and is licensed under the terms of the GNU Public License verson 3 or optionally any later version
// of the GNU Public License.  No warranty is provided and all warranties whatsoever are disclaimed.

#define Ziggurat_Rightmost_Position     3.44428647676

nosave private float array ziggurat_wtab = ({
    1.62318314817e-08, 2.16291505214e-08, 2.54246305087e-08, 2.84579525938e-08,
    3.10340022482e-08, 3.33011726243e-08, 3.53439060345e-08, 3.72152672658e-08,
    3.8950989572e-08, 4.05763964764e-08, 4.21101548915e-08, 4.35664624904e-08,
    4.49563968336e-08, 4.62887864029e-08, 4.75707945735e-08, 4.88083237257e-08,
    5.00063025384e-08, 5.11688950428e-08, 5.22996558616e-08, 5.34016475624e-08,
    5.44775307871e-08, 5.55296344581e-08, 5.65600111659e-08, 5.75704813695e-08,
    5.85626690412e-08, 5.95380306862e-08, 6.04978791776e-08, 6.14434034901e-08,
    6.23756851626e-08, 6.32957121259e-08, 6.42043903937e-08, 6.51025540077e-08,
    6.59909735447e-08, 6.68703634341e-08, 6.77413882848e-08, 6.8604668381e-08,
    6.94607844804e-08, 7.03102820203e-08, 7.11536748229e-08, 7.1991448372e-08,
    7.2824062723e-08, 7.36519550992e-08, 7.44755422158e-08, 7.52952223703e-08,
    7.61113773308e-08, 7.69243740467e-08, 7.77345662086e-08, 7.85422956743e-08,
    7.93478937793e-08, 8.01516825471e-08, 8.09539758128e-08, 8.17550802699e-08,
    8.25552964535e-08, 8.33549196661e-08, 8.41542408569e-08, 8.49535474601e-08,
    8.57531242006e-08, 8.65532538723e-08, 8.73542180955e-08, 8.8156298059e-08,
    8.89597752521e-08, 8.97649321908e-08, 9.05720531451e-08, 9.138142487e-08,
    9.21933373471e-08, 9.30080845407e-08, 9.38259651738e-08, 9.46472835298e-08,
    9.54723502847e-08, 9.63014833769e-08, 9.71350089201e-08, 9.79732621669e-08,
    9.88165885297e-08, 9.96653446693e-08, 1.00519899658e-07, 1.0138063623e-07,
    1.02247952126e-07, 1.03122261554e-07, 1.04003996769e-07, 1.04893609795e-07,
    1.05791574313e-07, 1.06698387725e-07, 1.07614573423e-07, 1.08540683296e-07,
    1.09477300508e-07, 1.1042504257e-07, 1.11384564771e-07, 1.12356564007e-07,
    1.13341783071e-07, 1.14341015475e-07, 1.15355110887e-07, 1.16384981291e-07,
    1.17431607977e-07, 1.18496049514e-07, 1.19579450872e-07, 1.20683053909e-07,
    1.21808209468e-07, 1.2295639141e-07, 1.24129212952e-07, 1.25328445797e-07,
    1.26556042658e-07, 1.27814163916e-07, 1.29105209375e-07, 1.30431856341e-07,
    1.31797105598e-07, 1.3320433736e-07, 1.34657379914e-07, 1.36160594606e-07,
    1.37718982103e-07, 1.39338316679e-07, 1.41025317971e-07, 1.42787873535e-07,
    1.44635331499e-07, 1.4657889173e-07, 1.48632138436e-07, 1.50811780719e-07,
    1.53138707402e-07, 1.55639532047e-07, 1.58348931426e-07, 1.61313325908e-07,
    1.64596952856e-07, 1.68292495203e-07, 1.72541128694e-07, 1.77574279496e-07,
    1.83813550477e-07, 1.92166040885e-07, 2.05295471952e-07, 2.22600839893e-07,
});

nosave private float array ziggurat_ytab = ({
    1, 0.963598623011, 0.936280813353, 0.913041104253,
    0.892278506696, 0.873239356919, 0.855496407634, 0.838778928349,
    0.822902083699, 0.807732738234, 0.793171045519, 0.779139726505,
    0.765577436082, 0.752434456248, 0.739669787677, 0.727249120285,
    0.715143377413, 0.703327646455, 0.691780377035, 0.68048276891,
    0.669418297233, 0.65857233912, 0.647931876189, 0.637485254896,
    0.62722199145, 0.617132611532, 0.607208517467, 0.597441877296,
    0.587825531465, 0.578352913803, 0.569017984198, 0.559815170911,
    0.550739320877, 0.541785656682, 0.532949739145, 0.524227434628,
    0.515614886373, 0.507108489253, 0.498704867478, 0.490400854812,
    0.482193476986, 0.47407993601, 0.466057596125, 0.458123971214,
    0.450276713467, 0.442513603171, 0.434832539473, 0.427231532022,
    0.419708693379, 0.41226223212, 0.404890446548, 0.397591718955,
    0.390364510382, 0.383207355816, 0.376118859788, 0.369097692334,
    0.362142585282, 0.355252328834, 0.348425768415, 0.341661801776,
    0.334959376311, 0.328317486588, 0.321735172063, 0.31521151497,
    0.308745638367, 0.302336704338, 0.29598391232, 0.289686497571,
    0.283443729739, 0.27725491156, 0.271119377649, 0.265036493387,
    0.259005653912, 0.253026283183, 0.247097833139, 0.241219782932,
    0.235391638239, 0.229612930649, 0.223883217122, 0.218202079518,
    0.212569124201, 0.206983981709, 0.201446306496, 0.195955776745,
    0.190512094256, 0.185114984406, 0.179764196185, 0.174459502324,
    0.169200699492, 0.1639876086, 0.158820075195, 0.153697969964,
    0.148621189348, 0.143589656295, 0.138603321143, 0.133662162669,
    0.128766189309, 0.123915440582, 0.119109988745, 0.114349940703,
    0.10963544023, 0.104966670533, 0.100343857232, 0.0957672718266,
    0.0912372357329, 0.0867541250127, 0.082318375932, 0.0779304915295,
    0.0735910494266, 0.0693007111742, 0.065060233529, 0.0608704821745,
    0.056732448584, 0.05264727098, 0.0486162607163, 0.0446409359769,
    0.0407230655415, 0.0368647267386, 0.0330683839378, 0.0293369977411,
    0.0256741818288, 0.0220844372634, 0.0185735200577, 0.0151490552854,
    0.0118216532614, 0.00860719483079, 0.00553245272614, 0.00265435214565,
});

nosave private int array ziggurat_ktab = ({
    0, 12590644, 14272653, 14988939,
    15384584, 15635009, 15807561, 15933577,
    16029594, 16105155, 16166147, 16216399,
    16258508, 16294295, 16325078, 16351831,
    16375291, 16396026, 16414479, 16431002,
    16445880, 16459343, 16471578, 16482744,
    16492970, 16502368, 16511031, 16519039,
    16526459, 16533352, 16539769, 16545755,
    16551348, 16556584, 16561493, 16566101,
    16570433, 16574511, 16578353, 16581977,
    16585398, 16588629, 16591685, 16594575,
    16597311, 16599901, 16602354, 16604679,
    16606881, 16608968, 16610945, 16612818,
    16614592, 16616272, 16617861, 16619363,
    16620782, 16622121, 16623383, 16624570,
    16625685, 16626730, 16627708, 16628619,
    16629465, 16630248, 16630969, 16631628,
    16632228, 16632768, 16633248, 16633671,
    16634034, 16634340, 16634586, 16634774,
    16634903, 16634972, 16634980, 16634926,
    16634810, 16634628, 16634381, 16634066,
    16633680, 16633222, 16632688, 16632075,
    16631380, 16630598, 16629726, 16628757,
    16627686, 16626507, 16625212, 16623794,
    16622243, 16620548, 16618698, 16616679,
    16614476, 16612071, 16609444, 16606571,
    16603425, 16599973, 16596178, 16591995,
    16587369, 16582237, 16576520, 16570120,
    16562917, 16554758, 16545450, 16534739,
    16522287, 16507638, 16490152, 16468907,
    16442518, 16408804, 16364095, 16301683,
    16207738, 16047994, 15704248, 15472926,
});

varargs float gaussian_number(float stddev) {
    float x, y;
    int sign;
    stddev ||= 1.0;
    for(;;) {
        int k1 = random(__INT_MAX__) + __INT_MIN__;
        int k2 = random(__INT_MAX__) + __INT_MIN__;
        int i = (k1 & 0xff);
        int j = (k2 & 0x00ffffff);
        sign = (i & 0x80) ? 1 : -1;
        i &= 0x7f;
        x = j * ziggurat_wtab[i];
        if(j < ziggurat_ktab[i])
            break;
        if(i < 127) {
            float y0 = ziggurat_ytab[i];
            float y1 = ziggurat_ytab[i + 1];
            float u1 = (random(__INT_MAX__) + __INT_MIN__) / to_float(__INT_MAX__);
            y = y1 + (y0 - y1) * u1;
        } else {
            float u1 = 1.0 - ((random(__INT_MAX__) + __INT_MIN__) / to_float(__INT_MAX__));
            float u2 = (random(__INT_MAX__) + __INT_MIN__) / to_float(__INT_MAX__);
            x = Ziggurat_Rightmost_Position - log(u1) / Ziggurat_Rightmost_Position;
            y = exp(-Ziggurat_Rightmost_Position * (x - 0.5 * Ziggurat_Rightmost_Position)) * u2;
        }
        if(y < exp(-0.5 * x * x))
            break;
    }
    return sign * stddev * x;
}

2
General / Heap fragmentation & you
« on: April 16, 2014, 01:13:11 PM »
FYI, if you're running an LPMud and operations seem to be getting inexplicably slower, with intermittent bursts of extreme slowness, you might be running into something that dogged me for a long time before I realized what it was: heap fragmentation.

Basically, your memory allocation is taking too long, sometimes way too long, because your free lists (the data structures that track what memory is available to be allocated) are full of wee little chunks and you're sometimes searching through enormous numbers of them to find a chunk of memory you can use.  So you get operations that were fast yesterday suddenly taking forever, and then not taking forever the next time you run them, and similar glorious debugging joys.

The fastest, most effective way to inflict horrific heap fragmentation on oneself that I've found is to quickly allocate enormous numbers of data structures, and immediately deallocate most of them but not all of them.  Intensive graph search in high combinatoric complexity spaces works great for this; what changed on Lost Souls that made me realize what was going on was a new system for procedural generation of descriptions and configurations for combinations of damage types.

Having realized what was up, and that we had been having the problem for a while without it getting bad enough to be properly identified until this new system was deployed, I was able to quickly get enormous performance benefits by 1) making said new system cache its final results to disk so the graph search didn't need to be recapitulated all the time, and 2) reducing the core lib's reliance on intermediate data structures, by which I mean arrays and mappings that are quickly used and discarded, usually for passing function results around.  Retaining and reusing, rather than reallocating, frequently used data structures, along with strategic use of returning complex results via pass by reference instead of data structure, seems to have quickly and dramatically reduced LS's vulnerability to heap fragmentation.  So I thought I'd pass along the information in case anybody else runs into the problem.

(Honestly, most people running LPMuds probably don't need to worry about the issue at all.  It's mostly really memory-intensive ones that would.  Lost Souls is, as far as I know, the only MUD to discover that things go horribly wrong with 32-bit LDMud's LPC parser if you try to address more than 2 gigabytes of memory.  If your MUD fits in a couple hundred megabytes, you'll be fine.)

3
Promotions / Call for articles for Imaginary Realities volume 6 issue 1
« on: December 22, 2013, 04:32:25 PM »
So, against all odds, Imaginary Realities has been resurrected!  We have our first issue of new content out, and are looking for articles for the second.

If you'd like to contribute, please take a look at our guide to the topics we're looking to have covered, and email Richard Tew, richard.m.tew@gmail.com, to verify that your topic is a good fit.  Articles should be in the range of 1000-4000 words, and need to be received by January 31st, 2014. Longer articles are possible for serialisation, with approval required.

4
At times, I have read people conversing as if they consider the pointer behavior displayed by mappings and arrays in all right-thinking LPMud drivers to be a problem that should be fixed.  In particular, I recall reference to copy-on-write schemes as a way of silently and irrevocably destroying this invaluable resource.  I believe such was implemented at some point in LDMud's development and quickly reverted, thanks be, and I suspect that the same may have happened to DGD, except without the reversion.  Woe.

I, of course, object to this horror on the same grounds that I hate the language I spend my professional life writing, PHP: I object to crippling competent programmers for the sake of mollycoddling incompetent ones.  (Some of why I still love LPC is that I've always felt it was reasonably successful at holding the hands of noobs without hamstringing experts.)

But, so I know exactly what LP driver variants to never touch and to take off my Christmas card list:

Who knows exactly which LP drivers use copy-on-write or some other mechanism to break pointer behavior with respect to mappings and/or arrays?

Let's include Pike there, though I can hardly credit the idea that Profezzorn would have done such a thing.  I can say of my own knowledge only that I'm certain that LDMud 3.2 does not do this.

5
Code Vault / wildmat_to_regexp()
« on: February 10, 2010, 09:26:17 AM »
Below is my code for generating a regular expression from a given wildmat-style match pattern.  This is useful for giving users the ability to specify patterns.  It uses Amylaar regreplace(); I have no particular idea whether something similar exists in MudOS-land.

This code is released into the public domain.

Quote
wildmat_to_regexp - converts a wildmat pattern to a regexp pattern

SYNOPSIS
    string wildmat_to_regexp(string pattern)

DESCRIPTION
    Converts a wildmat-style pattern (where ? is a single-character
    wildcard and * is a multiple-character wildcard) to a regular
    expression, returning the result.

SEE ALSO
    regexp(efun), regexplode(efun), regreplace(efun), regmatch(sefun)

Code: [Select]
string wildmat_to_regexp(string wildmat) {
    string out = wildmat;
    out = regreplace(out, "([\\[\\]\\(\\)\\|\\+\\.\\^\\$\\\\])", "\\\\1", 1);
    out = regreplace(out, "\\*", ".*", 1);
    out = regreplace(out, "\\?", ".", 1);
    out = "^" + out + "$"; 
    return out;
}

6
Code Vault / transform_sort() sefun
« on: October 17, 2009, 10:44:25 AM »
Below is my transform_sort() sefun, which Perl developers may recognize as an LPC implementation of the Schwartzian Transform.  The point of it is to sort arrays on potentially expensively derived criteria while performing the expensive operation only once per array element, instead of repeating it pointlessly and CPU-abusively.

The implementation uses Amylaar-style closures extensively, so will probably be difficult to port to FluffOS.

This code and documentation is released into the public domain.

Man page:

Quote
transform_sort - sorts an array based on a transformed version of its elements

SYNOPSIS
    mixed array transform_sort(mixed array list, mixed transform)
    mixed array transform_sort(mixed array list, mixed transform,
        closure comparator)

FILES
    /obj/master/sefun/arrays.c

DESCRIPTION
    Sorts the specified 'list' according to some transformed version of the
    elements present in it, determined by 'transform'.  'comparator' defines
    the comparison that will be used to determine the sort order; it defaults
    to #'>.

    'transform' can be:

    1) A closure, in which case the sort will be based on the values produced
    by the closure when called with each element of the array as argument.

    2) A mapping, in which case the sort will be based on, for each element in
    the array, the value found in the mapping for the element as key, or the
    element itself if it is not present in the mapping.

    3) A string, in which case the sort will be based on the value returned
    by call_other(element, transform) for each element of the array.  The array
    elements must be objects; unspecified errors will result otherwise.

    4) An array, in which case the sort will be based on the element in the
    corresponding position in this array.  In this case, 'transform' must be
    the same size as 'list'.

    This function provides no special protection against the case where the
    transformed values are not comparable.  This is to say, if it causes an
    error for the comparator in use to receive an integer and an object as
    its operands, this error will occur if the transformation you define makes
    us compare integers with objects.

    Whatever the transform is, it will be processed once, and only once, for
    each element of 'list'.  That means that potentially expensive operations
    that could potentially be called many times by a naive sort_array() call
    can usually be handled much more efficiently by this function.

EXAMPLES
    object array by_mass = transform_sort(all_inventory(), "query_real_mass");

    mixed array sorted = transform_sort(some_list, #'expensive_calculation);

    mixed array rev = transform_sort(some_list, #'expensive_calculation, #'<);
Code:
Code: [Select]
private int transform_sort_compare_greater_than(mixed array a, mixed array b) {
    return a[1] > b[1];
}

private int transform_sort_compare_less_than(mixed array a, mixed array b) {
    return a[1] < b[1];
}

private int transform_sort_compare_comparator(mixed array a, mixed array b, closure comparator) {
    return funcall(comparator, a[1], b[1]);
}

varargs mixed array transform_sort(mixed array list, mixed transform, closure comparator) {
    mixed array transformed;
    switch(typeof(transform)) {
    case T_CLOSURE  :
    case T_MAPPING  :
        transformed = map(list, transform);
        break;
    case T_STRING   :
        transformed = call_other(list, transform);
        break;
    case T_POINTER  :
        if(sizeof(transform) != sizeof(list))
            raise_error("Bad argument 2 to transform_sort()");
        transformed = transform;
        break;
    default         :
        raise_error("Bad argument 2 to transform_sort()");
    }
    if(!comparator || comparator == #'>)
        return map(sort_array(transpose_array(({ list, transformed })), #'transform_sort_compare_greater_than), #'[, 0);
    else if(comparator == #'<)
        return map(sort_array(transpose_array(({ list, transformed })), #'transform_sort_compare_less_than), #'[, 0);
    else
        return map(sort_array(transpose_array(({ list, transformed })), #'transform_sort_compare_comparator, comparator), #'[, 0);
}

7
Drivers / LDMud 3.2.16 released
« on: August 12, 2009, 12:55:20 PM »
reposted from ldmud-talk mailing list

Hello LDMud-Users,

we are pleased to announce the availability of a new old stable
LDMud release: Version 3.2.16.

As we announced a year ago the 3.2 branch is now in maintance mode
and will get only major bug fixed. So this release doesn't introduce
any new features but fixes a series of stack overflows and a few other
crashes.

To all users of the 3.2 series we recommend an upgrade to 3.3.719,
because 3.2.16 is very likely the last release in the 3.2 series.

LDMud 3.2.16 can be downloaded from:
http://www.bearnip.com/lars/proj/ldmud.html
http://www.bearnip.com/ftp/mud/ldmud-3.2.16.tar.gz
SHA1: b3c2a0ee44b2cf980ae1d0c4142c3e00088eb285
MD5: 3622a83dbbd16d8ba79686a348791f50

Please find below the most important changes. The detailed changes can be
found in CHANGELOG in the distribution package.

If you have any problems with LDMud, please don't hesitate to report them
at: http://mantis.bearnip.com/.
If you have any questions please ask either on ldmud-talk@lists.bearnip.com
or contact us at ldmud-dev@UNItopia.rus.Uni-Stuttgart.de.

What's new
==========

   - Fixed Crashers:
      + Removed a crasher when running with --check-refcounts: References
        by snooping non-interactives weren't counted (Bug #492).
      + An argument mismatch error during a lfun-closure call could
        crash the driver. (Bug #495).
      + Removed a possible stack overflow in restore_value (Bug #532).
      + save_object(), restore_object(), send_erq(), send_udp(),
        db_conv_string(), regexplode(), regexp(), present_clone()
        could crash the driver due to stack overflows, when large
        string were used as arguments.

   - Other Changes:
      + The mySQL configuration failed to handle less-standard standard
        paths correctly.
      + The autoconfiguration of YACC was incompatible with gentoo Linux.
      + Removed certain 64-bit issues (Bug #490, #491).
      + Removed an issue with local variable initialization after an
        inner block (Bug #537).

Have fun!
Fuchur@Wunderland, Gnomi@Unitopia and Zesstra@MorgenGrauen

8
Promotions / Lost Souls video
« on: May 01, 2009, 04:01:49 PM »
A player on LS made a combat into a VideoSpin video.

That is freakin' awesome.

9
Code Vault / scan_for_destructed_objects() sefun
« on: April 22, 2009, 07:18:10 PM »
This is my scan_for_destructed_objects() sefun, used to examine a complex data structure to cause any references to destructed objects within it to be examined by the driver, so that the reference will be freed and the destructed object hopefully allowed to be deallocated (when all other references to it are also cleaned up).

First argument is whatever we want to scan.  Second argument is optional, and should probably be left to internal use, but can be used to send an array of values that shouldn't be looked at, if encountered.  Return value is true if any circular references were encountered in the course of events (so this function can also be used to non-destructively check for the existence of circular references).

Both versions presume the existence of
Code: [Select]
#define True  1
#define False 0
#define Null  (-1)
which I keep in my system global include.  The LDmud version also needs
Code: [Select]
#define array *
This code is released into the public domain.

LDmud version:

Code: [Select]
varargs status scan_for_destructed_objects(mixed what, mixed array pointers) {
    pointers ||= ({});
    if(member(pointers, what) != Null)
        return True;
    status out = False;
    switch(typeof(what)) {
    case T_MAPPING                          :
        pointers += ({ what });
        switch(widthof(what)) {
        case 0                              :
            foreach(mixed key : what) {
                switch(typeof(key)) {
                case T_MAPPING              :
                case T_QUOTED_ARRAY         :
                case T_POINTER              :
                    if(scan_for_destructed_objects(key, pointers))
                        out = True;
                    break;
                }
            }
            break;
        case 1                              :
            foreach(mixed key, mixed val : what) {
                switch(typeof(key)) {
                case T_MAPPING              :
                case T_QUOTED_ARRAY         :
                case T_POINTER              :
                    if(scan_for_destructed_objects(key, pointers))
                        out = True;
                    break;
                }
                switch(typeof(val)) {
                case T_MAPPING              :
                case T_QUOTED_ARRAY         :
                case T_POINTER              :
                    if(scan_for_destructed_objects(val, pointers))
                        out = True;
                    break;
                }
            }
            break;
        default                             :
            int width = widthof(what);
            foreach(mixed key, mixed val : what) {
                switch(typeof(key)) {
                case T_MAPPING              :
                case T_QUOTED_ARRAY         :
                case T_POINTER              :
                    if(scan_for_destructed_objects(key, pointers))
                        out = True;
                    break;
                }
                switch(typeof(val)) {
                case T_MAPPING              :
                case T_QUOTED_ARRAY         :
                case T_POINTER              :
                    if(scan_for_destructed_objects(val, pointers))
                        out = True;
                    break;
                }
                for(int ix = width - 1; ix >= 2; ix--) {
                    mixed wval = what[key, ix];
                    switch(typeof(wval)) {
                    case T_MAPPING          :
                    case T_QUOTED_ARRAY     :
                    case T_POINTER          :
                        if(scan_for_destructed_objects(wval, pointers))
                            out = True;
                        break;
                    }
                }
            }
            break;
        }
        break;
    case T_QUOTED_ARRAY                     :
        do
            what = unquote(what);
        while(typeof(what) == T_QUOTED_ARRAY);
        // fallthrough
    case T_POINTER                          :
        pointers += ({ what });
        foreach(mixed item : what) {
            switch(typeof(item)) {
            case T_MAPPING                  :
            case T_QUOTED_ARRAY             :
            case T_POINTER                  :
                if(scan_for_destructed_objects(item, pointers))
                    out = True;
                break;
            }
        }
        break;
    }
    return out;
}

Untested attempt at a MudOS/FLuffOS port:

Code: [Select]
varargs status scan_for_destructed_objects(mixed what, mixed array pointers) {
    pointers ||= ({});
    if(member(pointers, what) != Null)
        return True;
    status out = False;
    switch(typeof(what)) {
    case T_MAPPING                          :
        pointers += ({ what });
        foreach(mixed key, mixed val : what) {
            switch(typeof(key)) {
            case T_MAPPING              :
            case T_POINTER              :
                if(scan_for_destructed_objects(key, pointers))
                    out = True;
                break;
            }
            switch(typeof(val)) {
            case T_MAPPING              :
            case T_POINTER              :
                if(scan_for_destructed_objects(val, pointers))
                    out = True;
                break;
            }
        }
        break;
    }
    case T_POINTER                          :
        pointers += ({ what });
        foreach(mixed item : what) {
            switch(typeof(item)) {
            case T_MAPPING                  :
            case T_POINTER                  :
                if(scan_for_destructed_objects(item, pointers))
                    out = True;
                break;
            }
        }
        break;
    }
    return out;
}

10
Code Vault / break_circular_references() sefun
« on: April 22, 2009, 07:11:24 PM »
This is my break_circular_references() sefun, used to examine a complex data structure for self-referentiality (which, in a refcounted garbage collection system like your common or garden variety LPmud, means memory leakage) and destroy the circular references, allowing the memory to be reclaimed when all other references to the data structure are gone.

First argument is whatever we want to check for circularity.  Second argument is optional, and should probably be left to internal use, but can be used to send an array of values that shouldn't be looked at, if encountered.

Both versions presume the existence of
Code: [Select]
#define True  1
#define False 0
#define Null  (-1)
which I keep in my system global include.  The LDmud version also needs
Code: [Select]
#define array *
#define keys  m_indices

This code is released into the public domain.

LDmud version:

Code: [Select]
varargs void break_circular_references(mixed value, mixed array pointers) {
    pointers ||= ({});
    switch(typeof(value)) {
    case T_POINTER                  :
        pointers += ({ value });
        for(int ix = sizeof(value) - 1; ix >= 0; ix--) {
            mixed what = value[ix];
            switch(typeof(what)) {
            case T_POINTER          :
            case T_MAPPING          :
                if(member(pointers, what) != Null)
                    value[ix] = 0;
                else
                    break_circular_references(what, pointers);
                break;
            case T_QUOTED_ARRAY     :
                do
                    what = unquote(what);
                while(typeof(what) == T_QUOTED_ARRAY);
                if(member(pointers, what) != Null)
                    value[ix] = 0;
                else
                    break_circular_references(what, pointers);
                break;
            }
        }
        break;
    case T_QUOTED_ARRAY             :
        do
            value = unquote(value);
        while(typeof(value) == T_QUOTED_ARRAY);
        pointers += ({ value });
        for(int ix = sizeof(value) - 1; ix >= 0; ix--) {
            mixed what = value[ix];
            switch(typeof(what)) {
            case T_POINTER          :
            case T_MAPPING          :
                if(member(pointers, what) != Null)
                    value[ix] = 0;
                else
                    break_circular_references(what, pointers);
                break;
            case T_QUOTED_ARRAY     :
                do
                    what = unquote(what);
                while(typeof(value) == T_QUOTED_ARRAY);
                if(member(pointers, what) != Null)
                    value[ix] = 0;
                else
                    break_circular_references(what, pointers);
                break;
            }
        }
        break;
    case T_MAPPING                  :
        pointers += ({ value });
        int width = widthof(value);
        mixed array keys = keys(value);
        for(int ix = sizeof(keys) - 1; ix >= 0; ix--) {
            status delete = False;
            mixed key = keys[ix];
            switch(typeof(key)) {
            case T_POINTER          :
            case T_MAPPING          :
                if(member(pointers, key) != Null)
                    delete = True;
                else
                    break_circular_references(key, pointers);
                break;
            case T_QUOTED_ARRAY     :
                do
                    key = unquote(key);
                while(typeof(keys) == T_QUOTED_ARRAY);
                if(member(pointers, key) != Null)
                    delete = True;
                else
                    break_circular_references(key, pointers);
                break;
            }
            for(int wix = 0; wix < width; wix++) {
                mixed what = value[key, wix];
                switch(typeof(what)) {
                case T_MAPPING      :
                case T_POINTER      :
                    if(member(pointers, what) != Null)
                        value[key, wix] = 0;
                    else
                        break_circular_references(what, pointers);
                    break;
                case T_QUOTED_ARRAY :
                    do
                        what = unquote(what);
                    while(typeof(keys) == T_QUOTED_ARRAY);
                    if(member(pointers, what) != Null)
                        value[key, wix] = 0;
                    else
                        break_circular_references(what, pointers);
                    break;
                }
            }
            if(delete)
                m_delete(value, key);
        }
        break;
    }
}

Untested attempt at a MudOS/FluffOS port:

Code: [Select]
varargs void break_circular_references(mixed value, mixed array pointers) {
    pointers ||= ({});
    switch(typeof(value)) {
    case T_POINTER                  :
        pointers += ({ value });
        for(int ix = sizeof(value) - 1; ix >= 0; ix--) {
            mixed what = value[ix];
            switch(typeof(what)) {
            case T_POINTER          :
            case T_MAPPING          :
                if(member(pointers, what) != Null)
                    value[ix] = 0;
                else
                    break_circular_references(what, pointers);
                break;
            }
        }
        break;
    case T_MAPPING                  :
        pointers += ({ value });
        mixed array keys = keys(value);
        for(int ix = sizeof(keys) - 1; ix >= 0; ix--) {
            status delete = False;
            mixed key = keys[ix];
            switch(typeof(key)) {
            case T_POINTER          :
            case T_MAPPING          :
                if(member(pointers, key) != Null)
                    delete = True;
                else
                    break_circular_references(key, pointers);
                break;
            }
            mixed what = value[key];
            switch(typeof(what)) {
            case T_MAPPING      :
            case T_POINTER      :
                if(member(pointers, what) != Null)
                    value[key] = 0;
                else
                    break_circular_references(what, pointers);
                break;
            }
            if(delete)
                m_delete(value, key);
        }
        break;
    }
}

11
Code Vault / search_data() sefun
« on: April 22, 2009, 07:02:56 PM »
This is my search_data() sefun, used to check for the presence of a specified value anywhere in a complex data structure (though it will also check equivalence with a simple value if it happens to be passed one, so it can be used in a fully general way).

First argument is what we're searching in, second argument is what we're looking for.  Third argument is optional (and mostly intended for internal use anyway, in coping with circular references) but can be used to send an array of values that should be excluded from consideration of any kind.

Both versions presume the existence of
Code: [Select]
#define True  1
#define False 0
#define Null  (-1)
which I keep in my system global include.  The LDmud version also needs
Code: [Select]
#define array *
This code is released into the public domain.

LDmud version:

Code: [Select]
#include <lpctypes.h>

varargs status search_data(mixed what, mixed find, mixed array skip) {
    skip ||= ({});
    if(member(skip, what) != Null)
        return False;
    if(what == find)
        return True;
    switch(typeof(what)) {
    case T_QUOTED_ARRAY :
        do 
            what = unquote(what);
        while(typeof(what) == T_QUOTED_ARRAY);
        if(what == find)
            return True;
        // fallthrough
    case T_POINTER      :
        skip += ({ what });
        foreach(mixed elem : what)
            if(search_data(elem, find, &skip))
                return True;
        return False;
    case T_MAPPING      :
        skip += ({ what });
        int width = widthof(what);
        switch(width) {
        case 0  : 
            foreach(mixed key : what)
                if(search_data(key, find, &skip))
                    return True;
            break;
        case 1  :
            foreach(mixed key, mixed value : what)
                if(search_data(key, find, &skip) || search_data(value, find, &skip))
                    return True;
            break;
        default :
            foreach(mixed key, mixed val0, mixed val1 : what) {
                if(search_data(key, find, &skip) || search_data(val0, find, &skip) || search_data(val1, find, &skip))
                    return True;
                for(int pos = 2; pos < width; pos++)
                    if(search_data(what[key, pos], find, &skip))
                        return True;
            }
            break;
        }
    }
    return False;
}

Untested attempt at MudOS/FluffOS port:

Code: [Select]
#include <lpctypes.h>

varargs status search_data(mixed what, mixed find, mixed array skip) {
    skip ||= ({});
    if(member(skip, what) != Null)
        return False;
    if(what == find)
        return True;
    switch(typeof(what)) {
    case T_POINTER      :
        skip += ({ what });
        foreach(mixed elem : what)
            if(search_data(elem, find, skip))
                return True;
        return False;
    case T_MAPPING      :
        skip += ({ what });
        foreach(mixed key, mixed value : what)
            if(search_data(key, find, skip) || search_data(value, find, skip))
                return True;
        break;
    }
    return False;
}

12
Code Vault / Supplementary trigonometric sefuns
« on: January 17, 2009, 12:23:25 PM »
Below is some code I wrote up today to help with some trigonometric operations.  Mainly, I needed to be able to tell the direction northwest that it's composed of north and west and have it figure out the angles it involves from there, hence the angle-averaging functions.  In the case where they're averaging two angles (rather than three or more), the averaging functions use some funny math based on the sine and cosine sum-to-product identities to evade the floating-point precision issues that otherwise creep in pretty quickly.

This code is released into the public domain.

My /lib/geometry.h is just included for this, which can go wherever you like:
Code: [Select]
// The ratio of a circle's circumference to its diameter.  Remember to change
// this to 3.0 if you move to a site in Indiana.

#define Pi                                      3.1415927

Code: [Select]
// Supplementary trigonometric sefuns module
//
// by Chaos, Sat Jan 17 13:10:24 EST 2009

#include "/lib/geometry.h"

varargs mixed gradian_add(varargs mixed array list) {
    mixed out = 0;
    foreach(mixed item : list)
        out += item;
    while(out >= 400)
        out -= 400;
    while(out < 0)
        out += 400;
    return out;
}

varargs mixed degree_add(varargs mixed array list) {
    mixed out = 0;
    foreach(mixed item : list)
        out += item;
    while(out >= 360)
        out -= 360;
    while(out < 0)
        out += 360;
    return out;
}

varargs float radian_add(varargs float array list) {
    float out = 0;
    foreach(mixed item : list)
        out += item;
    while(out >= 2 * Pi)
        out -= 2 * Pi;
    while(out < 0)
        out += 2 * Pi;
    return out;
}

float gradians_to_degrees(mixed val) {
    return val * 0.9;
}

float gradians_to_radians(mixed val) {
    return val * Pi / 200;
}

float degrees_to_gradians(mixed val) {
    return val * 200.0 / 180;
}

float degrees_to_radians(mixed val) {
    return val * Pi / 180;
}

float radians_to_gradians(float val) {
    return val * 200.0 / Pi;
}

float radians_to_degrees(float val) {
    return val * 180.0 / Pi;
}

varargs float gradian_average(varargs mixed array args) {
    switch(sizeof(args)) {
    case 0  :
        return 0;
    case 1  :
        return args[0];
    case 2  :
        {
            float a = args[0] * Pi / 400;
            float b = args[1] * Pi / 400;
            float c = a + b;
            float d = a - b;
            float out = atan2(sin(c) * cos(d), cos(c) * cos(d)) * 200 / Pi;
            while(out < 0)
                out += 400;
            return out;
        }
    default :
        {
            float sines = 0.0;
            float cosines = 0.0;
            foreach(float v : args) {
                v *= Pi / 200;
                sines += sin(v);
                cosines += cos(v);
            }
            float out = atan2(sines / sizeof(args), cosines / sizeof(args)) * 200 / Pi;
            while(out < 0)
                out += 400;
            return out;
        }
    }
}

varargs float degree_average(varargs mixed array args) {
    switch(sizeof(args)) {
    case 0  :
        return 0;
    case 1  :
        return args[0];
    case 2  :
        {
            float a = args[0] * Pi / 360;
            float b = args[1] * Pi / 360;
            float c = a + b;
            float d = a - b;
            float out = atan2(sin(c) * cos(d), cos(c) * cos(d)) * 180 / Pi;
            while(out < 0)
                out += 360;
            return out;
        }
    default :
        {
            float sines = 0.0;
            float cosines = 0.0;
            foreach(float v : args) {
                v *= Pi / 180;
                sines += sin(v);
                cosines += cos(v);
            }
            float out = atan2(sines / sizeof(args), cosines / sizeof(args)) * 180 / Pi;
            while(out < 0)
                out += 360;
            return out;
        }
    }
}

varargs float radian_average(varargs float array args) {
    switch(sizeof(args)) {
    case 0  :
        return 0;
    case 1  :
        return args[0];
    case 2  :
        {
            float c = (args[0] + args[1]) / 2;
            float d = (args[0] - args[1]) / 2;
            float out = atan2(sin(c) * cos(d), cos(c) * cos(d));
            while(out < 0)
                out += 2 * Pi;
            return out;
        }
    default :
        {
            float sines = 0.0;
            float cosines = 0.0;
            foreach(float v : args) {
                sines += sin(v);
                cosines += cos(v);
            }
            float out = atan2(sines / sizeof(args), cosines / sizeof(args));
            while(out < 0)
                out += 2 * Pi;
            return out;
        }
    }
}

13
General / Pike: what's the deal?
« on: December 30, 2008, 03:25:43 PM »
So we have this next-generation LPC-derivative scripting language, Pike, that's chock-full of LPC-ey goodness, doesn't have LPC's tragic anti-commercial licensing, and appears to have benefited from a good deal of business-use-driven development.

Has anyone written a MUD in it?  Has anyone tried to write a MUD in it?  Has anyone evaluated it for suitability for MUD usage?

You'd think that given the advantages, LPmud people would have flocked to it, but that doesn't appear to be the case, so I'm wondering if there's some big gotcha or set of small gotchas that make it a pain to use for MUDs.

14
Design Lab / Don't think. Profile.
« on: December 22, 2008, 12:16:11 AM »
So a while back, I started preferring this old-school inventory loop form:

Code: [Select]
for(object obj = first_inventory(whatever); obj; obj = next_inventory(env))
    do_whatever(obj);

over the modern

Code: [Select]
foreach(object obj : all_inventory(whatever))
    do_whatever(obj);

because, eyeballing it, I thought that the former would be faster because next_inventory() is a simple linked-list next-item link, doing a foreach() on all_inventory() makes the driver build a temporary array that it's just going to throw out, and so on.

For whatever reason, I just got around to actually testing this out, and discovered that the foreach() is about 17% faster.  Why, I can't imagine, but that's how it is.

Moral of the story: don't think, profile.

15
Code Vault / Services mechanism
« on: December 16, 2008, 12:37:24 AM »
Below is, I hope, all of the code implementing my 'services' mechanism, which is a method of using sefuns to accelerate access to well-known objects like system daemons and specific functions within them.  Its primary motivation is an examination of what happens when you do something like "/daemon/example"->foo(), which is fairly horrific (at least in LPmud and LDmud), with string reallocations before it even looks up /daemon/example and other such nonsense.  It also involves referring to those objects and functions by macros rather than strings, which I consider a benefit, both in visual aesthetics and insofar as typos become compile-time syntax errors instead of runtime errors.

This code is written for LDMud 3.2.15, and using it on another platform will require porting.

Current speed testing results on my system are:

Calling an undefined function in an object via string-LHS call_other() vs. calling the same undefined function in the same object via services-system-lookup LHS (example: "/daemon/weapons"->foo() vs. Daemon_Weapons->foo()): latter is 15% faster

Calling a fairly simple, defined function in an object via string-LHS call_other() vs. calling the same function in the same object via services-system-lookup LHS (example: "/daemon/weapons"->is_weapon_type(0) vs. Daemon_Weapons->is_weapon_type(0)): latter is 12% faster

Calling a fairly simple, defined function in an object via string-LHS call_other() vs. calling the same function in the same object via services system lookup direct to function (example: "/daemon/weapons"->is_weapon_type(0) vs. Is_Weapon_Type(0)): latter is 33% faster

Mostly, this is a way of helping a mudlib that heavily uses daemons (which generally becomes more prevalent as a lib design becomes more advanced) be less penalized in core system speed because of the execution path jumping between objects.  It derives this benefit by virtue of the sefun mechanism being considerably accelerated compared to call_other() to string LHSes.  If your driver does not have sefun acceleration like LDMud's or has better-optimized call_other()s, you may not see the same speed benefits.

The way the services system operates is by automatically generating header files like /lib/services/services_affiliations.h (for an object /daemon/affiliations) that contain macros for referring to the object and any functions it publishes.  The macros resolve to sefun calls producing the desired results.  (A master services file, /lib/services.h, containing macros for all the services the system tracks, is also published; this is better used for debugging than for production code, since it tends to become very long, to the point of seriously affecting object compile time.)  /lib/services/services_affiliations.h, on my system, looks like:

Code: [Select]
#ifndef _Services_Included
#ifndef _Services_Affiliations_Included
#define _Services_Affiliations_Included

// This file is maintained automatically by /daemon/services.c and should
// not be manually edited.  Changes made to this file will be lost.
//
// Further, do not *ever* use the service number identifiers in this file
// directly, or in any way rely on them to stay the same.  They *will* change.
// Use the macros.

#define Affiliation(a)                                          funcall(service(879), a)
#define Affiliation_Closure                                     service(879)
#define Affiliation_Maneuver_Access(a, b)                       funcall(service(880), a, b)
#define Affiliation_Maneuver_Access_Closure                     service(880)
#define Daemon_Affiliations                                     service(1)
#define Is_Affiliation(a)                                       funcall(service(881), a)
#define Is_Affiliation_Closure                                  service(881)

#endif
#endif

To start in on the actual code of the services system, first I'll include my /sys/global.h file, since it's referred to in files to follow and its macros are used extensively.  This file is configured to be universally included.

/sys/global.h:
Code: [Select]
#ifndef __GLOBAL_H__
#define __GLOBAL_H__

// Object namespace macros
#define Daemon(x)               ("/daemon/" + x)
#define Master_Object           __MASTER_OBJECT__

// Boolean and trinary values
#define True                    1
#define False                   0
#define Null                    (-1)

// Debugging macros
#define Debug(x)                (this_interactive() && this_interactive()->display(Daemon("debug")->dump_value(x)))
#define Debug_To(x, y)          (objectp(x) || find_object("/usr/" + (x)) ? (objectp(x) ? x : find_object("/usr/" + (x)))->display(Daemon("debug")->dump_value(y), 2) : 0)
#define Debug_For(x, y)         ((objectp(x) || find_object("/usr/" + (x))) && this_interactive() == (objectp(x) ? x : find_object("/usr/" + (x))) ? (objectp(x) ? x : find_object("/usr/" + (x)))->display(Daemon("debug")->dump_value(y), 2) : 0)
#define Debug_String(x)         Daemon("debug")->dump_value(x, 0, True)
#define Debug_Log(x)            log_file("lpmud.log", Debug_String(x))

// MudOS-alike macros
#define array                   *
#define arrayp                  pointerp
#define error                   raise_error
#define eval_cost               get_eval_cost
#define keys                    m_indices
#define map_delete              m_delete
#define new(x)                  clone_object(x)
#define values                  m_values

// Perl-alike pseudo control structures
#define unless(x)               if(!(x))
#define until(x)                while(!(x))

// pseudotype support
#define internal                private static
#define descriptor              mixed
#define struct                  mixed
#define structp                 pointerp
#define word_status(x)          ((x) ? "Yes" : "No")

// compat miscellany
#ifdef __LDMUD__
#define filter_array            filter
#define copy_mapping            copy
#define filter_mapping          filter
#define file_name               object_name
#define map_array               map
#else // __LDMUD__
#define abs(x)                  ((x) < 0 ? -(x) : (x))
#define set_environment         efun308
#define send_udp                send_imp
#define m_allocate              allocate_mapping
#define object_name             file_name
#endif // __LDMUD__

#endif // __GLOBAL_H__

/lib/service_conf.h, a configuration file for the services system.  It's assumed that this will be placed in some directory on your general include path.  Importantly, the Services macro defines the directories that should be scanned and services files produced for the objects in them, or individual files to be treated similarly.
Code: [Select]
#ifndef _Service_Conf_Included
#define _Service_Conf_Included

#define Services_File                 "/lib/services.h"
#define Services_Split_Dir            "/lib/services/"
#define Services                      ({\
    "/daemon",\
    "/def/descriptor",\
    __MASTER_OBJECT__ + ".c",\
})

#endif

/obj/master/sefun/services.c, the sefun module (inherited by /obj/master/simul_efun) that implements the support functions for the system.  Note: the start_simul_efun() function defined here needs to be called at the simul_efun object's create time.  /sys/lpctypes.h is, in my case, the lpctypes.h provided with LDMud.
Code: [Select]
// Services support module
//
// Factored from simul_efun.c by Chaos, Thu May 26 19:12:52 EDT 2005

#include "/sys/global.h"
#include "/sys/lpctypes.h"

string closure_name(closure func);
string printable(mixed val);

static mixed array services = ({});

void synchronize_services() {
    int size = Daemon("services")->query_services_count();
    if(sizeof(services) < size)
        services += allocate(size - sizeof(services));
}

status register_service() {
    object obj = previous_object();
    int identifier = Daemon("services")->query_service_identifier(obj);
    if(identifier == Null) {
        Daemon("services")->scan_services_locations();
        identifier = Daemon("services")->query_service_identifier(obj);
        if(identifier == Null)
            return False;
    }
    if(services[identifier])
        if(services[identifier] == obj)
            return True;
        else
            error("Duplicate service registration for " + printable(obj) + " vs. " + printable(services[identifier]));
    services[identifier] = obj;
    Daemon("services")->scan_service(obj);
    mixed array svc = obj->query_service_functions();
    if(svc) {
        string name;
        int id;
        closure func;
        foreach(mixed info : svc) {
            switch(typeof(info)) {
            case T_CLOSURE :
                name = closure_name(info);
                func = info;
                break;
            case T_POINTER :
                name = info[0];
                func = info[1];
                break;
            default        :
                error("Bad service function");
            }
            id = Daemon("services")->query_service_identifier(obj, name);
            if(id == Null)
                error("Could not find service identifier for function " + name + "() in " + printable(obj));
            services[id] = func;
        }
    }
    return True;
}

private mixed find_service(int identifier) {
    mixed location = Daemon("services")->query_service_location(identifier);
    if(!location)
        error("Service requested with unknown identifier " + identifier);
    string name = 0;
    sscanf(location, "%s::%s", location, name);
    object obj = load_object(location);
    Daemon("services")->scan_service(obj);
    if(name) {
        foreach(mixed func : obj->query_service_functions()) {
            switch(typeof(func)) {
            case T_CLOSURE :
                if(closure_name(func) == name)
                    return services[identifier] = func;
                break;
            case T_POINTER :
                if(func[0] == name)
                    return services[identifier] = func[1];
                break;
            default        :
                error("Bad service function");
            }
        }
        error("Cannot find function " + name + "() in service functions");
    } else {
        return services[identifier] = obj;
    }
}

mixed service(int identifier) {
    return services[identifier] || find_service(identifier);
}

mixed service_known(int identifier) {
    return services[identifier];
}

static void start_simul_efun() {
    if(find_object(Daemon("services")))
        synchronize_services();
}

/daemon/services.c, the main workhorse of the system.  Notes:
  • Daemon("reload_storage") is my mechanism for objects to temporarily store their state information in an external repository so it can be picked up by a later instance of themselves -- i.e. my way of making it so that daemons can be reloaded without losing their information.  You can just remove the lines referencing it without consequence.
  • The lines referencing Daemon("shutdown") are more important.  Daemon("shutdown") is, naturally, my handler for MUD shutdowns, and it supports being handed functions to call at shutdown time, as this daemon is doing.  Somehow or another, you need to do something equivalent and make sure that clear_services_files() is called right before the MUD shuts down, or bad things will happen.  (Mainly, you will probably get some objects compiled using services files from the previous runcycle, which is very likely to mean that the macros they were compiled with are completely wrong, which will cause all kinds of trouble.)
Code: [Select]
// Core system services access daemon
//
// by Chaos, Fri Mar 19 09:30:15 EST 2004

// This file does not and *must* not inherit /std/daemon or anything else,
// because if it does, its entire inheritance tree will be loaded before
// this file, which is bad, because to any extent that they use the services
// mechanism, they will either break or be loaded with slower code than
// would be the case if the services system were available.
//
// This file must not, of course, use services.h or any services_<name>.h
// file itself at all.

#include <files.h>
#include <functionlist.h>
#include <service_conf.h>

internal mapping files;
internal int count;

void verify_services_files();

void set_services_locations(mapping val) {
    files = val;
    count = 0;
    synchronize_services();
}

mapping query_services_locations() {
    return files;
}

void excise_service(string what) {
    map_delete(files, what);
}

string array clean_services() {
    string array out = ({});
    foreach(string what : files) {
        string file;
        string func;
        if(sscanf(what, "%s::%s", file, func) == 2) {
            closure array funcs = file->query_service_functions();
            if(!funcs || member(map(funcs, (: closurep($1) ? closure_name($1) : $1[0] :)), func) == Null)
                out += ({ what });
        } else {
            object obj = find_object(what);
            if(obj)
                continue;
            if(file_size(what) != FSIZE_NOFILE)
                continue;
            out += ({ what });
        }
    }
    if(sizeof(out)) {
        foreach(string what : out)
            map_delete(files, what);
        verify_services_files();
    }
    return out;
}

int query_services_count() {
    return count ||= sizeof(files) ? max(values(files)) + 1 : 0;
}

varargs int query_service_identifier(mixed target, string func) {
    if(objectp(target))
        target = object_name(target) + ".c";
    else if(!ends_with(target, ".c"))
        target += ".c";
    if(func)
        target += "::" + func;
    return query_services_locations()[target] || Null;
}

string query_service_location(int target) {
    foreach(string file, int identifier : query_services_locations())
        if(identifier == target)
            return file;
    return 0;
}

private string service_split_filename(string service) {
    if(ends_with(service, ".c"))
        service = service[..<3];
    return Services_Split_Dir + "services_" + explode(service, "/")[<1] + ".h";
}

private string service_location_split_filename(string location) {
    sscanf(location, "%s::%!s", location);
    return service_split_filename(location);
}

private string array service_functions(object obj) {
    string array out = ({});
    mixed array functions = obj->query_service_functions();
    if(functions) {
        foreach(mixed func : functions) {
            switch(typeof(func)) {
            case T_CLOSURE :
                out += ({ object_name(obj) + ".c::" + closure_name(func) });
                break;
            case T_POINTER :
                if(sizeof(func) == 2 && stringp(func[0]) && closurep(func[1])) {
                    out += ({ object_name(obj) + ".c::" + func[0] });
                    break;
                }
                // fallthrough
            default        :
                error("Bad service function from " + printable(obj) + ", " + printable(func));
            }
        }
    }
    return out;
}

private string array service_location_locate(string where) {
    string array out = ({});
    switch(file_size(where)) {
    case FSIZE_DIR    :
        if(member(where, '.') == -1) {
            if(where[<1] != '/')
                where += "/";
            foreach(string file : get_dir(where, GETDIR_PATH))
                out += service_location_locate(file);
        }
        break;
    case FSIZE_NOFILE :
        warn("tried to find service files in nonexistent target " + where);
        break;
    default           :
        if(!ends_with(where, ".c"))
            break;
        out += ({ where });
        object obj = find_object(where);
        if(obj)
            out += service_functions(obj);
        break;
    }
    return out;
}

private string array find_services_locations() {
    string array out = ({});
    foreach(string item : Services)
        out += service_location_locate(item);
    return out;
}

private void add_locations(string array add, mapping current) {
    int index = sizeof(current) ? max(values(current)) + 1 : 0;
    add = sort_array(add, #'>);
    foreach(string file : add)
        current[file] = index++;
    set_services_locations(current);
    verify_services_files();
}

void scan_services_locations() {
    string array add = ({});
    mapping current = query_services_locations();
    foreach(string location : find_services_locations())
        if(!member(current, location))
            add += ({ location });
    if(sizeof(add))
        add_locations(add, current);
}

void scan_service(object obj) {
    string array add = ({});
    mapping current = query_services_locations();
    foreach(string location : service_functions(obj))
        if(!member(current, location))
            add += ({ location });
    if(sizeof(add))
        add_locations(add, current);
}

private mapping retrieve_function_table(string file) {
    object obj;
    catch(obj = load_object(file));
    unless(obj)
        return ([]);
    int flags = TYPE_MOD_STATIC | TYPE_MOD_PRIVATE | TYPE_MOD_PROTECTED | NAME_HIDDEN;
    return mkmapping(functionlist(obj, flags | RETURN_FUNCTION_NAME), functionlist(obj, flags | RETURN_FUNCTION_NUMARG));
}

private varargs string service_file_header(string file, status interstitial) {
    if(ends_with(file, ".h"))
        file = file[..<3];
    if(member(file, '/') != Null)
        file = explode(file, "/")[<1];
    string macro = capitalize_words(regreplace(file, "[^A-Za-z0-9_]", "_", 1));
    string out = "";
    out += "#ifndef _Services_Included\n";
    if(file != "services")
        out += "#ifndef _" + macro + "_Included\n";
    out += "#define _" + macro + "_Included\n";
    out += "\n";
    out += "// This file is maintained automatically by /daemon/services.c and should\n";
    out += "// not be manually edited.  Changes made to this file will be lost.\n";
    out += "//\n";
    if(interstitial) {
        out += "// This is the interstitial version of the file, meant to be put in place\n";
        out += "// immediately before shutdown so that code compiled early in the boot\n";
        out += "// process is not hooked into the services mechanism using outdated service\n";
        out += "// ID numbers.\n";
    } else {
        out += "// Further, do not *ever* use the service number identifiers in this file\n";
        out += "// directly, or in any way rely on them to stay the same.  They *will* change.\n";
        out += "// Use the macros.\n";
    }
    out += "\n";
    return out;
}

private string service_file_footer(string file) {
    if(ends_with(file, ".h"))
        file = file[..<3];
    if(member(file, '/') != Null)
        file = explode(file, "/")[<1];
    string out = "";
    out += "\n";
    out += "#endif\n";
    if(file != "services")
        out += "#endif\n";
    return out;
}

mapping generate_services_content() {
    mapping files = ([]);
    string main = service_file_header(Services_File);
    mapping macro_action = ([]);
    mapping macro_split_filename = ([]);
    mapping macro_base = ([]);
    string action;
    string func;
    mapping tables = ([]);
    mapping table;
    mapping functions = ([]);
    status attach;
    if(file_size(Services_Split_Dir) != FSIZE_DIR)
        if(!mkdir(Services_Split_Dir))
            warn("cannot create services split directory " + printable(Services_Split_Dir));
    foreach(mixed location : query_services_locations())
        if(sscanf(location, "%s::%s", location, func))
            functions[func]++;
    int args;
    foreach(string location, int ident : query_services_locations()) {
        while(location[0] == '/')
            location = location[1..];
        func = 0;
        sscanf(location, "%s::%s", location, func);
        if(ends_with(location, ".c"))
            location = location[..<3];
        string split_filename = service_split_filename(location);
        if(func) {
            table = tables[location] ||= retrieve_function_table(location);
            args = table[func];
            if(functions[func] > 1) {
                location += "_" + func;
                attach = True;
            } else {
                location = func;
                attach = False;
            }
        }
        string base = capitalize_words(regreplace(location, "[^A-Za-z0-9_]", "_", 1));
        string macro = base;
        if(func) {
            unless(attach) {
                string macro_mod = macro + "_Closure";
                macro_action[macro_mod] = "service(" + ident + ")";
                macro_split_filename[macro_mod] = split_filename;
                macro_base[macro_mod] = macro_mod;
            }
            string array labels = ({});
            for(int index = 0; index < args; index++)
                labels += ({ sprintf("%c", 'a' + index) });
            if(args)
                macro += "(" + implode(labels, ", ") + ")";
            action = "funcall(service(" + ident + ")";
            if(args)
                action += ", " + implode(labels, ", ");
            action += ")";
            macro_action[macro] = action;
        } else {
            macro_action[macro] = "service(" + ident + ")";
        }
        macro_split_filename[macro] = split_filename;
        macro_base[macro] = base;
    }
    string array macros = sort_array(keys(macro_action), #'>);
    int width = sizeof(macros) ? max(map(macros, #'strlen)) : 0;
    foreach(string macro : macros) {
        action = macro_action[macro];
        string line;
        if(width + strlen(action) + 9 >= 132)
            line = "#define " + left_justify(macro, width) + " \\\n    " + action + "\n";
        else
            line = "#define " + left_justify(macro, width) + " " + action + "\n";
        string split_filename = macro_split_filename[macro];
        files[split_filename] ||= service_file_header(split_filename);
        files[split_filename] += line;
        main += "#ifndef " + macro_base[macro] + "\n";
        main += line;
        main += "#endif\n";
    }
    files[Services_File] = main;
    foreach(string file : files)
        files[file] += service_file_footer(file);
    return files;
}

mapping generate_interstitial_content() {
    mapping files = ([]);
    string main = service_file_header(Services_File, True);
    mapping macro_action = ([]);
    mapping macro_split_filename = ([]);
    mapping macro_base = ([]);
    string action;
    string func;
    mapping tables = ([]);
    mapping table;
    mapping functions = ([]);
    status attach;
    foreach(mixed location : query_services_locations())
        if(sscanf(location, "%s::%s", location, func))
            functions[func]++;
    int args;
    foreach(string location : query_services_locations()) {
        func = 0;
        sscanf(location, "%s::%s", location, func);
        if(ends_with(location, ".c"))
            location = location[..<3];
        string filename = location;
        if(filename[0] != '/')
            filename = "/" + filename;
        while(location[0] == '/')
            location = location[1..];
        string split_filename = service_split_filename(location);
        if(func) {
            table = tables[location] ||= retrieve_function_table(location);
            args = table[func];
            if(functions[func] > 1) {
                location += "_" + func;
                attach = True;
            } else {
                location = func;
                attach = False;
            }
        }
        string base = capitalize_words(regreplace(location, "[^A-Za-z0-9_]", "_", 1));
        string macro = base;
        if(func) {
            unless(attach) {
                string macro_mod = macro + "_Closure";
                macro_action[macro_mod] = "func(\"" + filename + "\", \"" + func + "\")";
                macro_split_filename[macro_mod] = split_filename;
                macro_base[macro_mod] = base;
            }
            string array labels = ({});
            for(int index = 0; index < args; index++)
                labels += ({ sprintf("%c", 'a' + index) });
            if(args)
                macro += "(" + implode(labels, ", ") + ")";
            action = "\"" + filename + "\"->" + func + "(";
            if(args)
                action += implode(labels, ", ");
            action += ")";
            macro_action[macro] = action;
        } else {
            macro_action[macro] = "load_object(\"" + filename + "\")";
        }
        macro_split_filename[macro] = split_filename;
        macro_base[macro] = base;
    }
    string array macros = sort_array(keys(macro_action), #'>);
    int width = sizeof(macros) ? max(map(macros, #'strlen)) : 0;
    foreach(string macro : macros) {
        action = macro_action[macro];
        string line;
        if(width + strlen(action) + 9 >= 132)
            line = "#define " + left_justify(macro, width) + " \\\n    " + action + "\n";
        else
            line = "#define " + left_justify(macro, width) + " " + action + "\n";
        string split_filename = macro_split_filename[macro];
        files[split_filename] ||= service_file_header(split_filename);
        files[split_filename] += line;
        main += "#ifndef " + macro_base[macro] + "\n";
        main += line;
        main += "#endif\n";
    }
    files[Services_File] = main;
    foreach(string file : files)
        files[file] += service_file_footer(file);
    return files;
}

void verify_services_files() {
    mapping files = generate_services_content();
    foreach(string file, string content : files) {
        if(content != read_file(file)) {
            rm(file);
            write_file(file, content);
        }
    }
}

private void clear_services_files() {
    mapping files = generate_interstitial_content();
    foreach(string file, string content : files) {
        if(content != read_file(file)) {
            rm(file);
            write_file(file, content);
        }
    }
}

void daemon_maintain_reset() {
    // This function is an asinine hack to forcibly suppress the even more asinine driver behavior that thinks objects don't need
    // reset() called if nothing has done a call_other() to them in some period of time.  If your driver doesn't do stupid things
    // like that, you don't need this.
    this_object()->load();
}

void preinit() {
    seteuid(getuid());
    object dmn = find_object(Daemon("reload_storage"));
    files = (dmn && dmn->retrieve_information(this_object())) || ([]);
    count = 0;
}

void create() {
    unless(sizeof(files))
        clear_services_files();
    scan_services_locations();
    synchronize_services();
    Daemon("shutdown")->add_shutdown_notify(#'clear_services_files);
}

void reset() {
    scan_services_locations();
    call_out("daemon_maintain_reset", 30);
}

void remove() {
    Daemon("reload_storage")->deposit_information(this_object(), files);
    Daemon("shutdown")->remove_shutdown_notify(#'clear_services_files);
    destruct(this_object());
}

object load() {
    return this_object();
}

That's just about it, then.  A couple last things:
  • Whatever objects are in your Services directories need to call the sefun register_service() from their create() in order to sync them up properly with the system.  I mostly accomplish this by having a /std/daemon that they inherit which handles the issue for them.
  • In order for your service objects to provide direct access to their functions, they need to define a function closure array query_service_functions() that returns an array of closures of the functions they want to publish.  For instance, this function as defined by my /daemon/affiliations, used as an example above, looks like:

Code: [Select]
closure array query_service_functions() {
    return ({
        #'is_affiliation,
        #'affiliation,
        #'affiliation_maneuver_access,
    });
}

Then, once the services daemon is loaded and generating its files, add /lib/services (or wherever you told it to put them) to your include path, #include <services_whatever.h>, and use the macros from it.

Pages: [1] 2 3