blob: 6b4bcc09d5ea5f67cabb2dd40e633a26a45df68b [file] [log] [blame] [raw]
package li.cil.oc
import java.io.File
import li.cil.oc.util.ExtendedConfiguration._
import li.cil.oc.util.PackedColor
object Config {
val resourceDomain = "opencomputers"
val namespace = "oc:"
val savePath = "opencomputers/"
val scriptPath = "/assets/" + resourceDomain + "/lua/"
// ----------------------------------------------------------------------- //
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
val screenDepthsByTier = Array(PackedColor.Depth.OneBit, PackedColor.Depth.FourBit, PackedColor.Depth.EightBit)
// ----------------------------------------------------------------------- //
var blockId1 = 3650
var blockId2 = 3651
var blockId3 = 3652
var blockId4 = 3653
var itemId = 4600
// ----------------------------------------------------------------------- //
// client
var maxScreenTextRenderDistance = 10.0
var screenTextFadeStartDistance = 8.0
var textLinearFiltering = false
var rTreeDebugRenderer = false // *Not* to be configurable via config file.
// ----------------------------------------------------------------------- //
// power
var ignorePower = false
var ratioIndustrialCraft2 = 2.0f
var ratioBuildCraft = 5.0f
var ratioUniversalElectricity = 0.05f
// power.buffer
var bufferCapacitor = 500.0
var bufferConverter = 100.0
var bufferChargingStation = 50000.0 // TODO
var bufferPowerSupply = 50.0
var bufferRobot = 10000.0
// power.cost
var computerCost = 1.0
var gpuFillCost = 1.0 / 100
var gpuClearCost = 1.0 / 400
var gpuCopyCost = 1.0 / 200
var gpuSetCost = 1.0 / 80
var hddReadCost = 1.0 / 1600.0
var hddWriteCost = 1.0 / 800.0
var powerSupplyCost = -1.25
var robotExhaustionCost = 1.0
var robotCost = 0.25
var robotMoveCost = 10.0
var robotTurnCost = 1.0
var screenCost = 0.1
var wirelessCostPerRange = 1.0 / 20.0
// ----------------------------------------------------------------------- //
// server
// server.computer
var baseMemory = 0
var canComputersBeOwned = true
var maxClipboard = 1024
var maxUsernameLength = 32
var maxUsers = 16
var startupDelay = 0.25
var threads = 4
var timeout = 1.0
// server.filesystem
var fileCost = 512
var bufferChanges = true
var maxHandles = 16
var maxReadBuffer = 8 * 1024
// server.misc
var commandUser = "OpenComputers"
var maxScreenHeight = 6
var maxScreenWidth = 8
var maxWirelessRange = 400.0
var rTreeMaxEntries = 10 // TODO config?
// ----------------------------------------------------------------------- //
// robot
var allowActivateBlocks = true
var allowUseItemsWithDuration = true
var canAttackPlayers = false
var canPlaceInAir = false
var itemDamageRate = 0.05
var swingRange = 0.49
var useAndPlaceRange = 0.65
// ----------------------------------------------------------------------- //
// robot.delays
var dropDelay = 0.1
var moveDelay = 0.4
var placeDelay = 0.3
var suckDelay = 0.1
var swingDelay = 0.4
var turnDelay = 0.4
var useDelay = 0.4
// ----------------------------------------------------------------------- //
def load(file: File) = {
val config = new net.minecraftforge.common.Configuration(file)
// --------------------------------------------------------------------- //
val Array(a, b, c, d) = config.fetch("block.ids", Array(blockId1, blockId2, blockId3, blockId4),
"""List of block IDs the mod uses for different types of blocks.""")
blockId1 = a
blockId2 = b
blockId3 = c
blockId4 = d
itemId = config.getItem("item", itemId,
"The item ID used for all non-stackable items.").
getInt(itemId)
// --------------------------------------------------------------------- //
config.getCategory("client").
setComment("Client side settings, presentation and performance related stuff.")
maxScreenTextRenderDistance = config.fetch("client.maxScreenTextRenderDistance", maxScreenTextRenderDistance,
"""|The maximum distance at which to render text on screens. Rendering text
|can be pretty expensive, so if you have a lot of screens you'll want
|to avoid huge numbers here. Note that this setting is client-sided, and
|only has an impact on render performance on clients.""".stripMargin)
screenTextFadeStartDistance = config.fetch("client.screenTextFadeStartDistance", screenTextFadeStartDistance,
"""|The distance at which to start fading out the text on screens. This is
|purely cosmetic, to avoid text disappearing instantly when moving too far
|away from a screen. This should have no measurable impact on performance.
|Note that this needs OpenGL 1.4 to work, otherwise text will always just
|instantly disappear when moving away from the screen displaying it.""".stripMargin)
textLinearFiltering = config.fetch("client.textLinearFiltering", textLinearFiltering,
"""|Whether to apply linear filtering for text displayed on screens when the
|screen has to be scaled down - i.e. the text is rendered at a resolution
|lower than their native one, e.g. when the GUI scale is less than one or
|when looking at a far away screen. This leads to smoother text for scaled
|down text but results in characters not perfectly connecting anymore (for
|example for box drawing characters. Look it up on Wikipedia.)""".stripMargin)
// --------------------------------------------------------------------- //
config.getCategory("power").
setComment("Power settings, buffer sizes and power consumption.")
ignorePower = config.fetch("power.ignorePower", ignorePower,
"""|Whether to ignore any power requirements. Whenever something requires
|power to function, it will try to get the amount of energy it needs from
|the buffer of its connector node, and in case it fails it won't perform
|the action / trigger a shutdown / whatever. Setting this to `true` will
|simply make the check 'is there enough energy' succeed unconditionally.
|Note that buffers are still filled and emptied following the usual rules,
|there just is no failure case anymore.""".stripMargin)
ratioIndustrialCraft2 = config.fetch("power.ratioIndustrialCraft2", ratioIndustrialCraft2,
"""|Conversion ratio for IndustrialCraft2's EU. This is how many internal
|energy units one EU generates.""".stripMargin)
ratioBuildCraft = config.fetch("power.ratioBuildCraft", ratioBuildCraft,
"""|Conversion ratio for BuildCraft's MJ. This is how many internal
|energy units one MJ generates.""".stripMargin)
ratioUniversalElectricity = config.fetch("power.ratioUniversalElectricity", ratioUniversalElectricity,
"""|Conversion ratio for Universal Electricity's Joules. This is how many
|internal energy units one Joule generates.""".stripMargin)
// --------------------------------------------------------------------- //
bufferCapacitor = config.fetch("power.buffer.capacitor", bufferCapacitor,
"""The amount of energy a capacitor can store.""") max 0
bufferConverter = config.fetch("power.buffer.converter", bufferConverter,
"""The amount of energy a power converter can store.""") max 0
bufferPowerSupply = config.fetch("power.buffer.powerSupply", bufferPowerSupply,
"""The amount of energy a power supply can store.""") max 0
bufferRobot = config.fetch("power.buffer.robot", bufferRobot,
"""The amount of power robots can store in their internal buffer.""") max 0
// --------------------------------------------------------------------- //
computerCost = config.fetch("power.cost.computer", computerCost,
"""The amount of energy a computer consumes per tick when running.""")
gpuFillCost = config.fetch("power.cost.gpuFill", gpuFillCost,
"""|Energy it takes to change a single 'pixel' via the fill command. This
|means the total cost of the fill command will be its area times this.""".stripMargin)
gpuClearCost = config.fetch("power.cost.gpuClear", gpuClearCost,
"""|Energy it takes to change a single 'pixel' to blank using the fill
|command. This means the total cost of the fill command will be its
|area times this.""".stripMargin)
gpuCopyCost = config.fetch("power.cost.gpuCopy", gpuCopyCost,
"""|Energy it takes to move a single 'pixel' via the copy command. This
|means the total cost of the copy command will be its area times this.""".stripMargin)
gpuSetCost = config.fetch("power.cost.gpuSet", gpuSetCost,
"""|Energy it takes to change a single 'pixel' via the set command. For
|calls to set with a string, this means the total cost will be the
|string length times this.""".stripMargin)
hddReadCost = config.fetch("power.cost.hddRead", hddReadCost,
"""|Energy it takes read a single byte from a file system. Note that non
|I/O operations on file systems such as `list` or `getFreeSpace` do
|*not* consume power.""".stripMargin)
hddWriteCost = config.fetch("power.cost.hddWrite", hddWriteCost,
"""Energy it takes to write a single byte to a file system.""")
powerSupplyCost = config.fetch("power.cost.powerSupply", powerSupplyCost,
"""|The amount of energy a power supply (item) produces per tick. This is
|basically just a consumer, but instead of taking energy it puts it
|back into the network. This is slightly more than what a computer
|consumes per tick. It's meant as an easy way to powering a small
|setup, mostly for testing.""".stripMargin)
robotExhaustionCost = config.fetch("power.cost.robotExhaustion", robotExhaustionCost,
"""|The conversion rate of exhaustion from using items to energy consumed.
|Zero means exhaustion does not require energy, one is a one to one
|conversion. For example, breaking a block generates 0.025 exhaustion,
|attacking an entity generates 0.3 exhaustion.""".stripMargin)
robotCost = config.fetch("power.cost.robot", robotCost,
"""|The amount of energy a robot consumes per tick when running. This is
|per default less than a normal computer uses because... well... they
|are better optimized? It balances out due to the cost for movement,
|interaction and whatnot, and the fact that robots cannot connect to
|component networks directly, so they are no replacements for normal
|computers.""".stripMargin)
robotMoveCost = config.fetch("power.cost.robotMove", robotMoveCost,
"""The amount of energy it takes a robot to move a single block.""")
robotTurnCost = config.fetch("power.cost.robotTurn", robotTurnCost,
"""The amount of energy it takes a robot to perform a 90 degree turn.""")
screenCost = config.fetch("power.cost.screen", screenCost,
"""|The amount of energy a screen consumes per displayed character per
|tick. If a screen cannot consume the defined amount of energy it will
|stop rendering the text that should be displayed on it. It will *not*
|forget that text, however, so when enough power is available again it
|will restore the previously displayed text (with any changes possibly
|made in the meantime). Note that for multi-block screens *each*
|screen that is part of it will consume this amount of energy per
|tick.""".stripMargin)
wirelessCostPerRange = config.fetch("power.cost.wirelessStrength", wirelessCostPerRange,
"""|The amount of energy it costs to send a signal with strength one,
|which means the signal reaches one block. This is scaled up linearly,
|so for example to send a signal 400 blocks a signal strength of 400
|is required, costing a total of 400 * `wirelessCostPerRange`. In
|other words, the higher this value, the higher the cost of wireless
|messages.[nl]
|See also: `maxWirelessRange`.""".stripMargin) max 0
// --------------------------------------------------------------------- //
config.getCategory("server").
setComment("Server side settings, gameplay and security related stuff.")
baseMemory = config.fetch("server.computer.baseMemory", baseMemory,
"""|The base amount of memory made available in computers even if they
|have no RAM installed. Use this if you feel you can't get enough RAM
|using the given means, that being RAM components. Just keep in mind
|that this is global and applies to all computers!""".stripMargin)
canComputersBeOwned = config.fetch("server.computer.canComputersBeOwned", canComputersBeOwned,
"""|This determines whether computers can only be used by players that
|are registered as users on them. Per default a newly placed computer
|has no users. Whenever there are no users the computer is free for
|all. Users can be managed via the Lua API (os.addUser, os.removeUser,
|os.users). If this is true, the following interactions are only
|possible for users:[nl]
| - input via the keyboard.[nl]
| - inventory management.[nl]
| - breaking the computer block.[nl]
|If this is set to false, all computers will always be usable by all
|players, no matter the contents of the user list. Note that operators
|are treated as if they were in the user list of every computer, i.e.
|no restrictions apply to them.[nl]
|See also: `maxUsers` and `maxUsernameLength`.""".stripMargin)
maxClipboard = config.fetch("server.computer.maxClipboard", maxClipboard,
"""|The maximum length of a string that may be pasted. This is used to
|limit the size of the data sent to the server when the user tries
|to paste a string from the clipboard (Shift+Ins on a screen with a
|keyboard).""".stripMargin)
maxUsernameLength = config.fetch("server.computer.maxUsernameLength", maxUsernameLength,
"""|Sanity check for username length for users registered with computers.
|We store the actual user names instead of a hash to allow iterating
|the list of registered users on the Lua side.[nl]
|See also: `canComputersBeOwned`.""".stripMargin) max 0
maxUsers = config.fetch("server.computer.maxUsers", maxUsers,
"""|The maximum number of users that can be registered with a single
|computer. This is used to avoid computers allocating unchecked
|amounts of memory by registering an unlimited number of users.
|See also: `canComputersBeOwned`.""".stripMargin) max 0
startupDelay = config.fetch("server.computer.startupDelay", startupDelay,
"""|The time in seconds to wait after a computer has been restored before
|it continues to run. This is meant to allow the world around the
|computer to settle, avoiding issues such as components in neighboring
|chunks being removed and then re-connected and other odd things that
|might happen.""".stripMargin) max 0
threads = config.fetch("server.computer.threads", threads,
"""|The overall number of threads to use to drive computers. Whenever a
|computer should run, for example because a signal should be processed
|or some sleep timer expired it is queued for execution by a worker
|thread. The higher the number of worker threads, the less likely it
|will be that computers block each other from running, but the higher
|the host system's load may become.""".stripMargin) max 1
timeout = config.fetch("server.computer.timeout", timeout,
"""|The time in seconds a program may run without yielding before it is
|forcibly aborted. This is used to avoid stupidly written or malicious
|programs blocking other computers by locking down the executor
|threads. Note that changing this won't have any effect on computers
|that are already running - they'll have to be rebooted for this to
|take effect.""".stripMargin) max 0
// --------------------------------------------------------------------- //
fileCost = config.fetch("server.filesystem.fileCost", fileCost,
"""|The base 'cost' of a single file or directory on a limited file
|system, such as hard drives. When computing the used space we add
|this cost to the real size of each file (and folders, which are zero
|sized otherwise). This is to ensure that users cannot spam the file
|system with an infinite number of files and/or folders. Note that the
|size returned via the API will always be the real file size, however.""".stripMargin) max 0
bufferChanges = config.fetch("server.filesystem.bufferChanges", bufferChanges,
"""|Whether persistent file systems such as disk drivers should be
|'buffered', and only written to disk when the world is saved. This
|applies to all hard drives. The advantage of having this enabled is
|that data will never go 'out of sync' with the computer's state if
|the game crashes. The price is slightly higher memory consumption,
|since all loaded files have to be kept in memory (loaded as in when
|the hard drive is in a computer).""".stripMargin)
maxHandles = config.fetch("server.filesystem.maxHandles", maxHandles,
"""|The maximum number of file handles any single computer may have open
|at a time. Note that this is *per filesystem*. Also note that this is
|only enforced by the filesystem node - if an add-on decides to be
|fancy it may well ignore this. Since file systems are usually
|'virtual' this will usually not have any real impact on performance
|and won't be noticeable on the host operating system.""".stripMargin) max 0
maxReadBuffer = config.fetch("server.filesystem.maxReadBuffer", maxReadBuffer,
"""|The maximum block size that can be read in one 'read' call on a file
|system. This is used to limit the amount of memory a call from a user
|program can cause to be allocated on the host side: when 'read' is,
|called a byte array with the specified size has to be allocated. So
|if this weren't limited, a Lua program could trigger massive memory
|allocations regardless of the amount of RAM installed in the computer
|it runs on. As a side effect this pretty much determines the read
|performance of file systems.""".stripMargin) max 0
// --------------------------------------------------------------------- //
commandUser = config.fetch("server.misc.commandUser", commandUser,
"""|The user name to specify when executing a command via a command
|block. If you leave this empty it will use the address of the network
|node that sent the execution request - which will usually be a
|computer.""".stripMargin).trim
maxScreenHeight = config.fetch("server.misc.maxScreenHeight", maxScreenHeight,
"""|The maximum height of multi-block screens, in blocks. This is limited
|to avoid excessive computations for merging screens. If you really
|need bigger screens it's probably safe to bump this quite a bit
|before you notice anything, since at least incremental updates should
|be very efficient (i.e. when adding/removing a single screen).""".stripMargin) max 1
maxScreenWidth = config.fetch("server.misc.maxScreenWidth", maxScreenWidth,
"""|The maximum width of multi-block screens, in blocks.[nl]
|See also: `maxScreenHeight`.""".stripMargin) max 1
maxWirelessRange = config.fetch("server.misc.maxWirelessRange", maxWirelessRange,
"""|The maximum distance a wireless message can be sent. In other words,
|this is the maximum signal strength a wireless network card supports.
|This is used to limit the search range in which to check for modems,
|which may or may not lead to performance issues for ridiculous
|ranges - like, you know, more than the loaded area.[nl]
|See also: `wirelessCostPerRange`.""".stripMargin) max 0
// --------------------------------------------------------------------- //
config.getCategory("robot").
setComment("Robot related settings, what they may do and general balancing.")
allowActivateBlocks = config.fetch("robot.allowActivateBlocks", allowActivateBlocks,
"""|Whether robots may 'activate' blocks in the world. This includes
|pressing buttons and flipping levers, for example. Disable this if
|it causes problems with some mod (but let me know!) or if you think
|this feature is too over-powered.""".stripMargin)
allowUseItemsWithDuration = config.fetch("robot.allowUseItemsWithDuration", allowUseItemsWithDuration,
"""|Whether robots may use items for a specifiable duration. This allows
|robots to use items such as bows, for which the right mouse button
|has to be held down for a longer period of time. For robots this works
|slightly different: the item is told it was used for the specified
|duration immediately, but the robot will not resume execution until
|the time that the item was supposedly being used has elapsed. This
|way robots cannot rapidly fire critical shots with a bow, for example.""".stripMargin)
canAttackPlayers = config.fetch("robot.canAttackPlayers", canAttackPlayers,
"""|Whether robots may damage players if they get in their way. This
|includes all 'player' entities, which may be more than just real
|players in the game.""".stripMargin)
canPlaceInAir = config.fetch("robot.canPlaceInAir", canPlaceInAir,
"""|Whether robots may place blocks in thin air, i.e. without a reference
|point (as is required for real players). Set this to true to emulate
|ComputerCraft's Turtles' behavior. When left false robots have to
|target an existing block face to place another block. For example,
|if the robots stands on a perfect plane, you have to call
|`robot.place(sides.down)` to place a block, instead of just
|`robot.place()`, which will default to `robot.place(sides.front)`.""".stripMargin)
itemDamageRate = config.fetch("robot.itemDamageRate", itemDamageRate,
"""|The rate at which items used as tools by robots take damage. A value
|of one means that items lose durability as quickly as when they are
|used by a real player. A value of zero means they will not lose any
|durability at all. This only applies to items that can actually be
|damaged (such as swords, pickaxes, axes and shovels).[nl]
|Note that this actually is the *chance* of an item losing durability
|when it is used. Or in other words, it's the inverse chance that the
|item will be automatically repaired for the damage it just took
|immediately after it was used.""".stripMargin) max 0 min 1
swingRange = config.fetch("robot.swingRange", swingRange,
"""|The 'range' of robots when swinging an equipped tool (left click).
|This is the distance to the center of block the robot swings the
|tool in to the side the tool is swung towards. I.e. for the collision
|check, which is performed via ray tracing, this determines the end
|point of the ray like so:[nl]
|`block_center + unit_vector_towards_side * swingRange`[nl]
|This defaults to a value just below 0.5 to ensure the robots will not
|hit anything that's actually outside said block.""".stripMargin)
useAndPlaceRange = config.fetch("robot.useAndPlaceRange", useAndPlaceRange,
"""|The 'range' of robots when using an equipped tool (right click) or
|when placing items from their inventory. See `robot.swingRange`. This
|defaults to a value large enough to allow robots to detect 'farmland',
|i.e. tilled dirt, so that they can plant seeds.""".stripMargin)
// ----------------------------------------------------------------------- //
// robot.delays
dropDelay = config.fetch("robot.delays.drop", dropDelay,
"""|The time in seconds to pause execution after an item was successfully
|dropped from a robot's inventory.""".stripMargin) max 0
moveDelay = config.fetch("robot.delays.move", moveDelay,
"""|The time in seconds to pause execution after a robot issued a
|successful move command. Note that this essentially determines how
|fast robots can move around, since this also determines the length of
|the move animation.""".stripMargin) max 0.05
placeDelay = config.fetch("robot.delays.place", placeDelay,
"""|The time in seconds to pause execution after a robot successfully
|placed an item from its inventory.""".stripMargin) max 0
suckDelay = config.fetch("robot.delays.suck", suckDelay,
"""|The time in seconds to pause execution after a robot successfully
|picked up an item after triggering a suck command.""".stripMargin) max 0
swingDelay = config.fetch("robot.delays.swing", swingDelay,
"""|The time in seconds to pause execution after a robot successfully
|swung a tool (or it's 'hands' if nothing is equipped). Successful in
|this case means that it hit something, either by attacking an entity
|or by breaking a block.""".stripMargin) max 0
turnDelay = config.fetch("robot.delays.turn", turnDelay,
"""|The time in seconds to pause execution after a robot turned either
|left or right. Note that this essentially determines hw fast robots
|can turn around, since this also determines the length of the turn
|animation.""".stripMargin) max 0.05
useDelay = config.fetch("robot.delays.use", useDelay,
"""|The time in seconds to pause execution after a robot successfully
|used an equipped tool (or it's 'hands' if nothing is equipped).
|Successful in this case means that it either used the equipped item,
|for example bone meal, or that it activated a block, for example by
|pushing a button.[nl]
|Note that if an item is used for a specific amount of time, like when
|shooting a bow, the maximum of this and the duration of the item use
|is taken.""".stripMargin) max 0
// --------------------------------------------------------------------- //
if (config.hasChanged) {
config.save()
}
}
}