Modding Discussion A bit of ranting from an old programmer (and some copyright law discussion)

Discussion in 'Starbound Modding' started by cpeosphoros, Jul 20, 2017.

  1. cpeosphoros

    cpeosphoros Orbital Explorer

    TL,DR: Comment your code, license it under MIT's.

    Guys (and girls, obviously), I've been programming for a long time, and, now, toying with SB's modding (yes, there is a mod or three coming from me any time soon), I'd like to rant a bit.

    Please, pretty please, comment your code, even if it seems obvious to you at the moment you write it. Even something like this:
    -- return a copy of the current scope
    function Scope:get ()
        -- PL's copy
        return tablex.deepCopy(self.current)
    Commenting your code makes it easier for other people to read it but you yourself will benefit from it, when, sometime six months from now you return to that code. It's easier to remember and search for "return a copy" or "current scope" than "tablex.deepCopy(self.current)". Moreso, your future self, or someone trying to grasp your code, may even ask, "WTH is tablex"?

    Also, as a best practice, comment while you are coding, not later. There is a concept in the software industry called "documentation debt". It goes as something like "I'll just hack this solution now. Later I'll comment it." and then, "later" never comes. More about that in this link:

    I don't mean you have to comment every line of your code, of course, but at least leave some clues for the people reading it in the future. Those people may include your future self. Well, 'nuff about that.

    My other point is licensing. License your code. It's as simple as sticking a LICENSE file in the root folder of your mod. If you don't know which one to choose, go with MIT's, Lua's been licensed under it for a long time and has not suffered from it. It allows for people to use and maintain your code, as long as they give credit to you. Here is an example of how I use it:
    All text pieces of this software with an "@author CPE" comment are released 
    under the MIT License, unless noted otherwise in their respective files or
    All non-text pieces of this software are released under the MIT License, except
    for those listed in the files LICENSE or  LICENSE.EXCEPTION, if any, in their 
    respective folders.
    Copyright (c) 2017 Chronos Phaenon Eosphoros
    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal in
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    And then I just put
    -- @licence MIT
    -- @author CPE
    at the start of any file I create, and make sure to attribute any third parties' work I include.

    But, please, pretty please, beautiful please, wonderful please, unless you are a professional coder or have a lawyer to advise you on that and really understand what you are doing, don't help proliferate the open source license jungle. I've seen lua code (not from Lua itself, but from third parties), licensed under GPL, LGPL, EPL, Apache, Mozilla, all flavors of CC, and a long etc. Please don't do that, just don't. The result of doing that is binding anyone who uses your code to stick to the same license all the way down the stream.

    And, besides the fact that most people will not have enough copyright law knowledge to really understand what those different forms of licensing actually mean, those who understand will be obliged to either segregate your code into a separate folder in order to license it under a different scheme than theirs, or simply move on and choose someone else's work to use instead of yours, because it's easier. I myself have done that a lot in my career.

    If you want to know more about licensing, this is a great resource: What they say about repositories actually applies to any software bundle. As your mod, for an example.

    So, that is it. Let's return to what's really nice to do. Coding.
    bk3k, Inf_Wolf14, IHart and 2 others like this.
  2. Errors4l

    Errors4l Spaceman Spiff

    Technical debt, yeah I definitely recognize that from working with other students at college... Can't say that I haven't done it either, though.

    Out of curiosity, how would/should you treat licensed assets that must be at specific locations? Not being able to contain them in a folder together with the license can make things a bit confusing.
    Take the following example. MIT licensed ItemScripts has the following files:
    Mod B embeds the assets of ItemScripts in it's own mod, making sure to include the license as well (renamed to ItemScripts-LICENSE).

    The problem here is that it becomes hard to keep track of what files are licensed under the ItemScripts license, and what files are not. People could easily think the itemScriptLoader.lua asset would be licensed under /LICENSE, rather than /ItemScripts-LICENSE.
    The @license MIT trick helps, and I'll probably start including it in my own scripts. If there are multiple (MIT) licenses involved you'd still not know which MIT license goes with it, though. I guess the @Author comment partially solves that issue?
  3. katana

    katana Scruffy Nerf-Herder

    That's not something that commenting should be handling - descriptive, terse variable naming should handle that issue. Outside of docblocks, which are for documenting things related to using functions/classes/etc, you should be commenting why. Not what, nor how....comment why.
    What and how can be observed by reading the code (presuming that it is well written) - why is what often needs documented, especially when things get..."arcane".

    In this case, the question of "WTH is tablex" should be answered with "What can I rename tablex into to make it self-descriptive?"

    Whilst I agree in the preference of MIT license for code, I must say that you should not be advising against specific licenses at all; that is a very broad brush to paint with, especially when you assume that the only thing that a mod can license is code. This is something each individual developer must learn about and decide what to use on their own.

    That is what @LICENSE and @Author header comments are for. Similarly, for smaller licenses, you can even embed the license in the file itself, much like I did with SMTk here:

    The tricky question is that of not code, but assets - PNG assets, particularly. That is where, instead of incorporating it into your mod, you make your mod a dependency of the other mod or make a standalone mod containing just the third party assets under license XYZ you depend on. This is where Starbound's _metadata[requires] functionality comes in particularly useful, even.
  4. Errors4l

    Errors4l Spaceman Spiff

    The MIT license basically allows you to do whatever you want, as long as you give attribution. This means that even if the intended usage is to add the mod as a dependency, it doesn't mean people aren't allowed / shouldn't be able to embed the assets. I know dependencies are the better solution, but some users generally suck at installing multiple mods to get something working (a rant topic that sometimes pops up on Discord too).

    The way I set up some of my library/toolkit mods is that the modder can choose whether they want to embed the assets, or include the mod (as a pak or a link to the mod) in their releases. The first makes the process more user friendly, while the latter is the more proper way that also makes things easier on the development / licensing side. Some people (users, not modders) simply don't grasp the concept of dependencies, and end up asking for help because they didn't read the giant letters telling them they need another mod. Making multiple mods purely to license things better would only worsen this. To use my mod, you need to install A and B as well. Keep in mind that B also has it's own dependencies you need to install!
  5. katana

    katana Scruffy Nerf-Herder

    That would be far easier were Starbound built on top of something with proper package management, true. I've seen Steam implement it well on the Workshop, but outside of that, it's eh.
  6. Errors4l

    Errors4l Spaceman Spiff

    Steam actually makes it more difficult, because the required items aren't actually enforced. It simply shows a dialog before subscribing (that users frequently skip, for some reason).
    When users tell me the mod crashes their game (the most common issue people have with my mods), I can usually assume that they forgot to install the required dependency both me and Steam have warned them about in multiple places.

    On GitHub I release zip files containing my mod and any dependencies the mod might have (though describing clearly where users can find the latest versions of the dependencies). Although the amount of subscriptions on the Steam Workshop is drastically bigger than the amount of downloads on GitHub, I can say I rarely ever have anyone reporting this same issue outside of the Workshop.
  7. katana

    katana Scruffy Nerf-Herder

    Odd, I could have sworn that it did that for the Arma3 mods I'd grabbed. Nvm me then.

    Maybe a proper mod management platform's necessary for Starbound mods...
  8. cpeosphoros

    cpeosphoros Orbital Explorer

    Who hasn't?
    You may include this inside the exception LICENSE file (in your example ItemScripts-LICENSE):
    This license applies only to those assets:
    - assetso.ext
    - assetsoandso.ext2
    - etc
    All other files in this folder are released under the LICENSE file terms, unless stated otherwise elsewhere.
    Actually, only your MIT license will apply to the code you release. You only have to attribute the last one in the chain. Here are some real examples of how I handle that:
    -- @author: CPE
    -- The LuaFileSystem (lfs) library is Copyright © 2003 Kepler Project
    In this case, lfs, being so old, it is not licensed under MIT's, but a very similar license. That's why I included the link to their license file.
    -- @author: CPE
    -- Algorithms from
    -- adapted to Lua 5.3 and heavily refactored.
    In this case the source is not actually a software, but an article with some interesting algorithms.
    -- @author: CPE
    -- Some parts of this module were adapted from Penlight's tablex module, which
    -- can be found at
    -- It's also released under MIT's license and is
    -- Copyright (C) 2009-2016 Steve Donovan, David Manura.
    Here we are talking about real software. In this case, I use a "-- Adapted from Penlight" comment inside the actual code where it applies. If you browse Penlight's source (it's an interesting exercise, very good code), you'll see that they too use other people's code, with a similar method of attribution.
    Errors4l likes this.
  9. cpeosphoros

    cpeosphoros Orbital Explorer

    I'm not sure, as I've never released a mod on Steam, but I think that's the difference between the "Require", "Dependencies", and "Include" fields. Maybe @bk3k may help us with that.
  10. IHart

    IHart Scruffy Nerf-Herder

    Question. You include the license in the project AND have every file point to the license?
  11. Errors4l

    Errors4l Spaceman Spiff

    Starbound dependencies (includes and requires) are enforced, and directly affect the load order and 'bootability' of the game. Steam Workshop dependencies simply trigger warnings when the user doesn't have them.
    The problem kicks in when people ignore these warnings. Starbound crashes on launch, showing an error that the dependency isn't found. I think a big part of the problem has to do with the fact that this error isn't very user friendly-- at all.


    The "Caused by:" line is the only line of any importance to the end user. If users skip the big warning, they're probably going to read over that line as well.
    [Error] Fatal Exception caught: (ApplicationException) Application threw exception during startup
    [0] 7ff6ede17153 Star::captureStack
    [1] 7ff6ede15ede Star::StarException::StarException
    [2] 7ff6ede15fc8 Star::StarException::StarException
    [3] 7ff6ee557dc0 Star::ApplicationException::ApplicationException
    [4] 7ff6ee80f252 `Star::SdlPlatform::SdlPlatform'::`1'::catch$275
    [5] 7ffd0dabc2e0 _C_specific_handler
    [6] 7ffd0dab2a23 _FrameUnwindFilter
    [7] 7ffd38939603 RtlCaptureContext
    [8] 7ff6ee558040 Star::SdlPlatform::SdlPlatform
    [9] 7ff6ee55ac22 Star::runMainApplication
    [10] 7ff6edd5ff76 WinMain
    [11] 7ff6ee748daf __scrt_common_main_seh
    [12] 7ffd35e42774 BaseThreadInitThunk
    [13] 7ffd38900d51 RtlUserThreadStart
    Caused by: (StarException) Asset source 'SpawnableItemPack' is missing dependency 'Manipulated UI'
    [0] 7ff6ede17153 Star::captureStack
    [1] 7ff6ede15ede Star::StarException::StarException
    [2] 7ff6ede16225 Star::StarException::StarException
    [3] 7ff6ee1ca208 <lambda_dd839ce1444433733421897c86cbef3a>::operator()
    [4] 7ff6ee1cbd3c std::_Func_impl<<lambda_dd839ce1444433733421897c86cbef3a>,std::allocator<int>,void,std::shared_ptr<`Star::Root::scanForAssetSources'::`2'::AssetSource> >::_Do_call
    [5] 7ff6ee1d2d4c Star::Root::scanForAssetSources
    [6] 7ff6ee1c9565 <lambda_629bd0625747976ff7eb77559f7c4f59>::operator()
    [7] 7ff6ee1bae8f std::_Invoker_functor::_Call<<lambda_629bd0625747976ff7eb77559f7c4f59> & __ptr64>
    [8] 7ff6ee1beda6 std::invoke<<lambda_629bd0625747976ff7eb77559f7c4f59> & __ptr64>
    [9] 7ff6ee1bc649 std::_Invoke_ret<std::shared_ptr<Star::Assets>,<lambda_629bd0625747976ff7eb77559f7c4f59> & __ptr64>
    [10] 7ff6ee1cb886 std::_Func_impl<<lambda_629bd0625747976ff7eb77559f7c4f59>,std::allocator<int>,std::shared_ptr<Star::Assets> >::_Do_call
    [11] 7ff6ee1ca337 std::_Func_class<std::shared_ptr<Star::VehicleDatabase> >::operator()
    [12] 7ff6ee1bff53 Star::Root::loadMemberFunction<Star::Assets>
    [13] 7ff6ee1cd545 Star::Root::assets
    [14] 7ff6edd5ba67 Star::ClientApplication::startup
    [15] 7ff6ee558040 Star::SdlPlatform::SdlPlatform
    [16] 7ff6ee55ac22 Star::runMainApplication
    [17] 7ff6edd5ff76 WinMain
    [18] 7ff6ee748daf __scrt_common_main_seh
    [19] 7ffd35e42774 BaseThreadInitThunk
    [20] 7ffd38900d51 RtlUserThreadStart
    Caught at:
    [0] 7ff6ede17153 Star::captureStack
    [1] 7ff6ede17714 Star::fatalException
    [2] 7ff6ee80f5dc `Star::runMainApplication'::`1'::catch$76
    [3] 7ffd0dabc2e0 _C_specific_handler
    [4] 7ffd0dab2a23 _FrameUnwindFilter
    [5] 7ffd38939603 RtlCaptureContext
    [6] 7ff6ee55ac22 Star::runMainApplication
    [7] 7ff6edd5ff76 WinMain
    [8] 7ff6ee748daf __scrt_common_main_seh
    [9] 7ffd35e42774 BaseThreadInitThunk
    [10] 7ffd38900d51 RtlUserThreadStart
  12. katana

    katana Scruffy Nerf-Herder

    Yeah, SB stack traces are *very* developer oriented. Not very modder or user friendly, tbh. Try having a frames file specify the wrong size for an asset - the resulting error message is rather bonkers.
    IHart likes this.
  13. cpeosphoros

    cpeosphoros Orbital Explorer

    I beg to disagree. Firts, refer to Doxygen is the de facto standard for documenting code in the professional software industry. This is a doc comment for a class, from a professional software package (In this case Java 8's JDK), written in compliance with that standard:
     * Writes text to a character-output stream, buffering characters so as to
     * provide for the efficient writing of single characters, arrays, and strings.
     * <p> The buffer size may be specified, or the default size may be accepted.
     * The default is large enough for most purposes.
     * <p> A newLine() method is provided, which uses the platform's own notion of
     * line separator as defined by the system property <tt>line.separator</tt>.
     * Not all platforms use the newline character ('\n') to terminate lines.
     * Calling this method to terminate each output line is therefore preferred to
     * writing a newline character directly.
     * <p> In general, a Writer sends its output immediately to the underlying
     * character or byte stream.  Unless prompt output is required, it is advisable
     * to wrap a BufferedWriter around any Writer whose write() operations may be
     * costly, such as FileWriters and OutputStreamWriters.  For example,
     * <pre>
     * PrintWriter out
     *   = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
     * </pre>
     * will buffer the PrintWriter's output to the file.  Without buffering, each
     * invocation of a print() method would cause characters to be converted into
     * bytes that would then be written immediately to the file, which can be very
     * inefficient.
     * @see PrintWriter
     * @see FileWriter
     * @see OutputStreamWriter
     * @see java.nio.file.Files#newBufferedWriter
     * @author      Mark Reinhold
     * @since       JDK1.1
    Function doc comments are not very different.
    Not always. This is a sample of real Lua code, with all comments stripped out. I suppose it's good code, since it's slightly adapted from a very renowned Lua programmer. No wanting to be rude, please tell me what it does.
    local function createPipe(first, second)
        local _names = first._name .."|" .. second._name
        local function _function( pInput, pOutput)
            local coRout = coroutine.create( first.fCall )
            local function pFunction()
                local result = { coroutine.resume( coRout, first, pInput, coroutine.yield) }
                local success = table.remove(result, 1)
                if success then
                    if coroutine.status( coRout ) == "suspended" then
                        return table.unpack( result )
                    myError:error( "\n" .. result[ 1 ] )
            local result = {second:fCall( pFunction, pOutput )}
            return table.unpack(result)
        local pipe = Filter({_function = _function , _name = _names})
        return pipe
    local pipeLevel = 1
    function Filter:pipe(...)
        pipeLevel = pipeLevel + 1
        local filters = {...}
        if #filters == 0 then
            myError:error("You must specify at least one filter.", 2)
        local filter = table.remove(filters, 1)
        local pipe = createPipe(self, filter)
        if #filters ~= 0 then
            pipe = pipe:pipe(table.unpack(filters))
        pipeLevel = pipeLevel - 1
        return pipe
    Well, I agree with the general concept that code should describe itself. The problem is the chance of someone reading your code not having enough programming experience to understand it is high. That's more true in any modding community, not only Starbound's.
    Most people start dabbling with modding from a textures/json perspective, which is relatively easy (comparing to scripting). When they try to move to scripting, "code should describe itself" is a very harsh entrance barrier.
    On the other hand, when that same inexperienced people manage to pass through that barrier an actually start coding, their code is anything but good code to read and understand. Hence the need for comments.
    Please mind I'm not being prejudicial against new coders, here. On the contrary, they are very welcome. We all have been there once. But it is a fact of life that new coder's code is most, if not all, of the time very ugly. And they will have a very hard time leaving that stage if "good code should describe itself".
    Actually, the professional software industry, and moreso the open source community, has updated that concept for soome time now. It's current version could be phrased as "good code should describe itself and be well commented".
    One answer to that question could be "the company I work for has a very strict variable naming policy and I can't call it extendedTableUtilities". There are many possible others.
    I couldn't agree more. That's why I said in the OP: "If you don't know which one to choose, go with MIT's" and "unless you are a professional coder or have a lawyer to advise you on that and really understand what you are doing".
    I'm not assuming that. What I'm assuming is that the vast majority of new modders are no experienced with copyright law. If you are a graphic artist you'll probably understand what CC is, how it works, etc, and will apply the proper version to your sprites. Same for sounds, etc. But if you are not, MIT's is not perfect for those, but will at least give your work some copyright coverage until you learn more about specialized licenses.
    Well, they are not for that. They are for documenting your code.
    Actually, that's not a good practice and is advised against. You sure can put small licenses, or resumed versions of larger ones, that is not the problem. But, in a coding community, not having a LICENSE file, even for very small projects, even one file projects, is bad. In real life professional programming, most people will skip a project without even bothering to open specific files if it doesn't have a LICENSE file.
    The legal definition of software includes all accompanying material. So, if you release your mod under MIT's the sound, image, etc, assets, will also be released under MIT's, as accompanying material, unless you make an exception for them (e.g. to give them a CC license).
    Actually, if you use their assets, even by dependency inclusion, you are subject to their licensing scheme for the parts of your work which depends on theirs. Using someone else's work, be it by inclusion or dependency, in a derivative work you will publish is what is called conveying in copyright law parlance. That may mean a range of things, but in most cases it will mean you are obliged to release your work under the same license as theirs, or to clearly mark the boundaries between your derivative work and theirs original and then attribute the right license to each part.
  14. cpeosphoros

    cpeosphoros Orbital Explorer

    I include LICENSE once int the root folder and use @license in each and every file. That's not common practice, but it's what my company's lawyer instructed us to do.
    IHart likes this.
  15. katana

    katana Scruffy Nerf-Herder

    That is a docblock - as I'd mentioned, that's a different case. Inline documentation however, should be why as I'd mentioned.

    True enough. I'd have to agree - my FOSS/professional experience has moved me along that path, whereas the novices probably still need to comment heavily (if only for their sake). :)

    That's...not entirely true. Having comments that explain why, and be self-descriptive, is okay - having comments that explain what you are doing actually adds maintenance burdens and potential for future issues; often, code gets modified, but comments that detail how a thing works (let's say we describe a for loop that iterates X times, and someone changes it to X-1 times) often fall behind, aren't updated, and then later serve to confuse future developers. Can't tell you how many times I've run into that situation and asked "Is this how it should be working? Was this supposed to iterate X times and someone messed up making changes, or was it changed after the fact deliberately...?" So, can't entirely agree there - there definitely needs to be a line drawn in terms of what exactly you're commenting to document.

    Understandable - though in that case, that's when a company's policies interfere with good development practices. :\

    Last I saw that has not not been hashed out in the courts; the definition of "derivative work" is malleable at best, and especially when it comes to scripting languages versus compiled, the waters become even more muddied.

    Intent was to state you can have it in both places (and should, really). Having a standard header comment across your files with project name, copyright info, license, authorship, and the file's purpose (possibly even any todos relevant to that file) should be a dev's standard practice. This way, if the piece is separated from the whole, the license and authorship are still documented and it's not possible without deliberate user modification to lose it.

    Though that doesn't stop some projects like OpenCart from stealing other open source code, stripping licenses, and claiming it as their own...but that's another matter!
  16. cpeosphoros

    cpeosphoros Orbital Explorer

    Well, I entirely agree. I think I didn't understand you at first, and you me, Lol. What you are describing as "why vs. what" is what I meant with "I don't mean you have to comment every line of your code, of course, but at least leave some clues for the people reading it in the future.". I think we were on the same page on that all the time, after all.

    As if it never happened... :(
    That depends on which country's court, or in some case which state's, you are talking about. Depending on whose work you are grabbing upon, or on who you work for, it's better be safe than sorry.
    Heck, not being entirely cynical, in some places whose work you grabbed and who you work for will determine the result of the court case.
    Yes, that's what I usually do, too. I just don't like having the license's text inside my code, even as small commented out resumed versions. That's why we use just the custom @license tag and a link to the license if it's different from ours. For other people's code I just use the IDE's fold functionality.
    Yes, don't even start me on that... Lol...
  17. cpeosphoros

    cpeosphoros Orbital Explorer

    That's not needed.

    Suppose those scenarios (by depends I mean either a "require" statement or actually including the files in your project's folder):
    1. You are making mod A, which entirely depends on someone else's work B and optionally on another someone else's work C.
    1.1 B and C have the same or very similar licenses - Use either license.
    1.2 B and C have different but compatible licenses and B's is stricter - Use B's license.
    1.3 B and C have different but compatible licenses and C's is stricter - You have 2 options:
    1.3.1 - Release A under C's license.
    1.3.2 - Release A under B's license, with an exception for the (clearly marked) optional parts which depend on C. Those will carry C's license.
    1.4. B and C have different but incompatible licenses. You can't use either license, therefore you can't publish A unless you get express and previous, preferably written, authorization from both B and C's authors.

    2. You are making mod A, which depends on your own work B and optionally on someone's else work C.
    It's almost the same as the scenario 1, above, but as you can choose B's license, be sure to choose one which is compatible with C's and gives the sub-scenario you want.

    3. You are making mod A, which entirely depends on someone else's work B and entirely on another someone else's work C.
    If B and C's licenses are compatible but not the same, use the stricter one. If they are not compatible, it's the same case as scenario 1.4 above.

    Scenarios with more than 2 dependencies may be reduced to those by applying them to each pair of dependencies until you are left with only 2.

    In any scenario, if you can use a license you can use a compatible stricter one in place of it.

    All that is from a modder/developer/client/boss's perspective. From the user/player's perspective, nothing of that probably matters.

    If you want to learn about licenses compatibility and strictness, has links pointing to that information on the most used ones. For the less used, you should refer to a lawyer before even touching the works they apply to.

    As @katana said:
    But courts' opinions change with place and time and in some places they also change depending on who is involved in the case in hand. So, better safe then sorry: if your work depends on someone else's, it's a derivative (by the stricter definition). Publishing a derivative is conveying their work, and should comply with their license.

    Notice that using a software tool (Like an IDE, graphic tool, etc) doesn't turn the work made in them a derivative of theirs. So, using ZeroBrane to edit a script will not require the script to follow ZeroBrane's code licenses. Making a code editor based on ZB's code would, even if you use Sublime to edit it.
    Errors4l likes this.
  18. bk3k

    bk3k Oxygen Tank

    "includes" is enforced only as a means of fixing load order. And I'm not sure the very word is all that descriptive for how it behaves. If you put other mods in the "includes" array, your mod will load after those mods (if those mods are present). But missing those mods won't prevent Starbound from starting up, and they won't trigger an error in your logs. If you really needed the other mod(s), it doesn't make for a good choice because later you'll be missing assets. But it is good choice when you're adding in optional compatibility/expansion thanks to the controlled load order.

    "requires" is enforced by preventing Starbound from fully loading when one or more listed mods is missing. It will also make sure your mod loads after the listed mods.
  19. Errors4l

    Errors4l Spaceman Spiff

    Enforced may not have been the best term to use. What I mean is, that both the includes and requires parameters in metadata files make Starbound behave differently: they will affect the way Starbound handles loading the files.
    Steam mod dependencies on the other hand don't change anything. Whether you require a mod or not on Steam, you can launch the game just fine without any warning (even if you have a Steam mod that has a dependency, which you don't have).
  20. IHart

    IHart Scruffy Nerf-Herder

    Licensing technical question has been irking me for a while: Is it safe to call a mod that consists of a single patch file "software". I feel like if the license is read as the legal jargon it is that that term would not really apply.

Share This Page