Skip to content
Browse files

unusable. client/server migration

  • Loading branch information...
1 parent 1d72806 commit 02cbef2b4c52e23b0f8b0a50870743705a1a1646 @bitcraft committed May 17, 2012
View
0 cleanup.sh 100755 → 100644
File mode changed.
View
165 lib/renderer-pw14.py
@@ -0,0 +1,165 @@
+from lib2d.tilemap import BufferedTilemapRenderer
+from lib2d.objects import AvatarObject
+from pygame import Rect, draw
+
+import weakref
+
+
+def screenSorter(a):
+ return a[-1].x
+
+
+class LevelCamera(object):
+ """
+ Base class for displaying maps. Not really featured, here, you should
+ subclass it.
+ """
+
+ def __init__(self, area, rect, tmxdata=None):
+ rect = Rect(rect)
+ self.rect = rect
+ self.area = area
+ self.set_extent(rect)
+
+ # create a renderer for the map
+ self.maprender = BufferedTilemapRenderer(tmxdata, rect)
+ self.map_width = tmxdata.tilewidth * tmxdata.width
+ self.map_height = tmxdata.tileheight*tmxdata.height
+ self.blank = True
+
+ # add the avatars
+ #for child in self.area.getChildren():
+ ## if isinstance(child, AvatarObject):
+ # child.avatar.update(0) # hack to re-init avatar
+
+ self.ao = self.refreshAvatarObjects()
+
+
+ def refreshAvatarObjects(self):
+ return [ i for i in self.area.bodies.keys() if hasattr(i, "avatar")]
+
+
+ # HACK
+ def getAvatarObjects(self):
+ if self.area.changedAvatars:
+ self.area.changedAvatars = False
+ self.ao = self.refreshAvatarObjects()
+ return self.ao
+
+
+ def set_extent(self, extent):
+ """
+ the camera caches some values related to the extent, so it becomes
+ nessessary to call this instead of setting the extent directly.
+ """
+
+ self.extent = Rect(extent)
+ self.half_width = self.extent.width / 2
+ self.half_height = self.extent.height / 2
+ self.width = self.extent.width
+ self.height = self.extent.height
+ self.zoom = 1.0
+
+
+ def update(self, time):
+ self.maprender.update(None)
+ for ao in self.refreshAvatarObjects():
+ if ao:
+ # HACK
+ try:
+ ao.avatar.update(time)
+ except:
+ pass
+
+
+ def center(self, pos):
+ """
+ center the camera on a pixel location.
+ """
+
+ x, y = self.toSurface(pos)
+
+ if self.map_width > self.width:
+ if x < self.half_width:
+ x = self.half_width
+
+ elif x > self.map_width - self.half_width - 1:
+ x = self.map_width - self.half_width - 1
+
+ else:
+ x = self.map_width / 2
+
+
+ if self.map_height > self.height:
+ if y < self.half_height:
+ y = self.half_height
+
+ elif y > self.map_height - self.half_height:
+ y = self.map_height - self.half_height
+ else:
+ y = self.map_height / 2
+
+ self.extent.center = (x, y)
+ self.maprender.center((x, y))
+
+
+ def clear(self, surface):
+ raise NotImplementedError
+
+
+ def draw(self, surface):
+ avatarobjects = self.refreshAvatarObjects()
+
+ onScreen = []
+
+ if self.blank:
+ self.blank = False
+ self.maprender.blank = True
+
+ # quadtree collision testing would be good here
+ for a in avatarobjects:
+ bbox = self.area.getBBox(a)
+ x, y, z, d, w, h = bbox
+ x, y = self.toSurface((x, y, z))
+ xx, yy = a.avatar.axis
+ x += xx
+ y += yy + h
+ if self.extent.colliderect((x, y, w, h)):
+ onScreen.append((a.avatar.image, Rect(self.toScreen((x, y)), (w, h)), 1, a, bbox))
+
+ # should not be sorted every frame
+ onScreen.sort(key=screenSorter)
+
+ return self.maprender.draw(surface, onScreen)
+
+ dirty = self.maprender.draw(surface, onScreen)
+
+ clip = surface.get_clip()
+ surface.set_clip(self.rect)
+
+ for (x, y, w, h) in self.area.geoRect:
+ draw.rect(surface, (128,128,255), \
+ (self.toScreen(self.toSurface((0, x, y))), (w, h)), 1)
+
+ for i, r, l, a in onScreen:
+ x, y, z, d, w, h, = self.area.getBBox(a)
+ x, y = self.toScreen(self.toSurface((x, y, z+h)))
+ draw.rect(surface, (255,128,128), (x, y, w, h), 1)
+
+ surface.set_clip(clip)
+ return dirty
+
+
+ def toScreen(self, (x, y)):
+ """
+ Transform the world to coordinates on the screen
+ """
+
+ return (x * self.zoom - self.extent.left + self.rect.left,
+ y * self.zoom - self.extent.top + self.rect.top)
+
+
+ def toSurface(self, pos):
+ """ Translate world coordinates to coordinates on the surface """
+ return pos[1], pos[2]
+
View
7 lib/titlescreen.py
@@ -24,6 +24,13 @@
import os.path, time
+from lib2d.client.client import Client
+
+client = Client("leif", "pass", "127.0.0.1", 25565)
+client.login()
+
+
+
class SubPixelThread(Thread):
"""
Process a subpixel image in the background.
View
21 lib2d/client/client.py
@@ -1,3 +1,5 @@
+from lib2d.client.net.tcp import TCPClient
+from lib2d.common.packets import make_packet
@@ -7,4 +9,23 @@ class Client(object):
"""
+ version = 1
+ def __init__(self, user, passwd, addr, port):
+ self.user = user
+ self.passwd = passwd
+ self.connect()
+
+
+ def connect(self):
+ self.conn = TCPClient()
+ self.conn.connect("127.0.0.1", 25565)
+
+
+ def login(self):
+ packet = make_packet("login",
+ protocol=self.version,
+ username=self.user,
+ password=self.passwd)
+
+ self.conn.send_data(packet)
View
10 lib2d/client/net/communicate.py
@@ -1,6 +1,4 @@
from errors import *
-try: import cPickle as pickle
-except: import pickle as pickle
import zlib
def EncodeData(data,compress):
@@ -10,10 +8,14 @@ def EncodeData(data,compress):
length = str(len(data))
length = ("0"*(8-len(length)))+length
return length,data
+
+
def DecodeData(data):
try:data = pickle.loads(data)
except:data = pickle.loads(zlib.decompress(data))
return data
+
+
def SendData(sock,data,compress,includelength=False,address=None):
length,data = EncodeData(data,compress)
if includelength: data = length + data
@@ -26,6 +28,8 @@ def SendData(sock,data,compress,includelength=False,address=None):
except:
sock.close()
raise SocketError("Connection is broken; data could not be sent!")
+
+
def ReceiveData(sock):
try:
length = int(sock.recv(8))
@@ -35,6 +39,8 @@ def ReceiveData(sock):
raise SocketError("Connection is broken; data could not be received!")
data = DecodeData(data)
return data
+
+
def ReceiveDataUDP(sock,size=1024):
try:
data, address = sock.recvfrom(size)
View
32 lib2d/client/net/netbase.py
@@ -1,7 +1,9 @@
import socket,select,sys,time
from errors import *
from communicate import SendData, ReceiveData, ReceiveDataUDP
-
+
+
+
class TCPServer():
def __init__(self):
self.sending_socket = None
@@ -70,6 +72,7 @@ def quit(self):
for s in self.connected_sockets: s.close()
self.quit_func(self.host,self.port)
+
class UDPServer():
def __init__(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -108,33 +111,8 @@ def send_data(self,data,compress=False):
def quit(self):
self.socket.close()
self.quit_func(self.host,self.port)
-
-class TCPClient:
- def __init__(self):
- pass
- def connect(self,host,port):
- self.host = host
- self.port = port
- try:
- self.socket = socket.socket()
- self.socket.connect((self.host,self.port))
- except:
- self.socket.close()
- raise SocketError("The connection could not be opened. It must be created first with a server object.")
-
- def send_data(self,data,compress=False):
- SendData(self.socket,data,compress,includelength=True)
- def wait_for_data(self):
- input_ready,output_ready,except_ready = select.select([self.socket],[],[])
- return ReceiveData(self.socket)
- def check_for_data(self):
- input_ready,output_ready,except_ready = select.select([self.socket],[],[],0.001)
- if len(input_ready) > 0:
- return ReceiveData(self.socket)
-
- def quit(self):
- self.socket.close()
+
class UDPClient:
def __init__(self):
pass
View
0 lib2d/client/net/socket.py → lib2d/client/net/socket-2.py
File renamed without changes.
View
36 lib2d/client/net/tcp.py
@@ -0,0 +1,36 @@
+import socket, select
+
+
+
+class TCPClient:
+ def __init__(self):
+ pass
+
+
+ def connect(self, host, port):
+ self.host = host
+ self.port = port
+ self.socket = socket.create_connection((self.host, self.port))
+
+
+ def send_data(self, data):
+ try:
+ self.socket.send(data)
+ except:
+ raise
+ self.sock.close()
+
+
+ def wait_for_data(self):
+ input_ready,output_ready,except_ready = select.select([self.socket],[],[])
+ return ReceiveData(self.socket)
+
+
+ def check_for_data(self):
+ input_ready,output_ready,except_ready = select.select([self.socket],[],[],0.001)
+ if len(input_ready) > 0:
+ return ReceiveData(self.socket)
+
+
+ def quit(self):
+ self.socket.close()
View
36 lib2d/common/encodings.py
@@ -0,0 +1,36 @@
+from codecs import (BufferedIncrementalDecoder, CodecInfo, IncrementalEncoder,
+ StreamReader, StreamWriter, utf_16_be_encode,
+ utf_16_be_decode)
+
+def ucs2(name):
+ if name.lower() not in ("ucs2", "ucs-2"):
+ return None
+
+ def ucs2_encode(input, errors="replace"):
+ input = u"".join(i if ord(i) < 65536 else u"?" for i in input)
+ return utf_16_be_encode(input, errors)
+
+ ucs2_decode = utf_16_be_decode
+
+ class UCS2IncrementalEncoder(IncrementalEncoder):
+ def encode(self, input, final=False):
+ return ucs2_encode(input, self.errors)[0]
+
+ class UCS2IncrementalDecoder(BufferedIncrementalDecoder):
+ _buffer_decode = ucs2_decode
+
+ class UCS2StreamWriter(StreamWriter):
+ encode = ucs2_encode
+
+ class UCS2StreamReader(StreamReader):
+ decode = ucs2_decode
+
+ return CodecInfo(
+ name="ucs2",
+ encode=ucs2_encode,
+ decode=ucs2_decode,
+ incrementalencoder=UCS2IncrementalEncoder,
+ incrementaldecoder=UCS2IncrementalDecoder,
+ streamwriter=UCS2StreamWriter,
+ streamreader=UCS2StreamReader,
+ )
View
36 lib2d/server/packets.py → lib2d/common/packets.py
@@ -11,6 +11,15 @@
from construct import StringAdapter, LengthValueAdapter, Sequence
+DUMP_ALL_PACKETS = 1
+
+# Strings.
+# This one is a UCS2 string, which effectively decodes single writeChar()
+# invocations. We need to import the encoding for it first, though.
+from lib2d.common.encodings import ucs2
+from codecs import register
+register(ucs2)
+
class DoubleAdapter(LengthValueAdapter):
@@ -30,10 +39,11 @@ def AlphaString(name):
)
+
+
dimensions = {
"earth": 0,
"sky": 1,
- "nether": 255,
}
dimension = Enum(UBInt8("dimension"), **dimensions)
@@ -65,15 +75,7 @@ def AlphaString(name):
1: Struct("login",
UBInt32("protocol"),
AlphaString("username"),
- SBInt64("seed"),
- Enum(UBInt32("mode"),
- survival=0,
- creative=1,
- ),
- dimension,
- UBInt8("difficulty"),
- UBInt8("height"),
- UBInt8("players"),
+ AlphaString("password"),
),
2: Struct("handshake",
AlphaString("username"),
@@ -141,6 +143,18 @@ def AlphaString(name):
}
+packet_stream = Struct("packet_stream",
+ OptionalGreedyRange(
+ Struct("full_packet",
+ UBInt8("header"),
+ Switch("payload", lambda context: context["header"], packets),
+ ),
+ ),
+ OptionalGreedyRange(
+ UBInt8("leftovers"),
+ ),
+)
+
packets_by_name = dict((v.name, k) for (k, v) in packets.iteritems())
@@ -198,7 +212,7 @@ def parse_packets_incrementally(bytestream):
yield header, payload
-def makePacket(packet, *args, **kwargs):
+def make_packet(packet, *args, **kwargs):
"""
Constructs a packet bytestream from a packet header and payload.
View
73 lib2d/common/temporal.py
@@ -0,0 +1,73 @@
+from twisted.internet.defer import Deferred
+from twisted.python.failure import Failure
+
+"""
+Time-related utilities.
+"""
+
+class PendingEvent(object):
+ """
+ An event which will happen at some point.
+
+ Structurally, this could be thought of as a poor man's upside-down
+ DeferredList; it turns a single callback/errback into a broadcast which
+ fires many multiple Deferreds.
+
+ This code came from Epsilon and should go into Twisted at some point.
+ """
+
+ def __init__(self):
+ self.listeners = []
+
+ def deferred(self):
+ d = Deferred()
+ self.listeners.append(d)
+ return d
+
+ def callback(self, result):
+ l = self.listeners
+ self.listeners = []
+ for d in l:
+ d.callback(result)
+
+ def errback(self, result=None):
+ if result is None:
+ result = Failure()
+ l = self.listeners
+ self.listeners = []
+ for d in l:
+ d.errback(result)
+
+
+def split_time(timestamp):
+ """
+ Turn an MC timestamp into hours and minutes.
+
+ The time is calculated by interpolating the MC clock over the standard
+ 24-hour clock.
+
+ :param int timestamp: MC timestamp, in the range 0-24000
+ :returns: a tuple of hours and minutes on the 24-hour clock
+ """
+
+ # 24000 ticks per day
+ hours, minutes = divmod(timestamp, 1000)
+
+ # 6:00 on a Christmas morning
+ hours = (hours + 6) % 24
+ minutes = minutes * 6 // 100
+
+ return hours, minutes
+
+def timestamp_from_clock(clock):
+ """
+ Craft an int-sized timestamp from a clock.
+
+ More precisely, the size of the timestamp is 4 bytes, and the clock must
+ be an implementor of IReactorTime. twisted.internet.reactor and
+ twisted.internet.task.Clock are the primary suspects.
+
+ This function's timestamps are millisecond-accurate.
+ """
+
+ return int(clock.seconds() * 1000) & 0xffffffff
View
8 lib2d/config.py
@@ -1,6 +1,8 @@
from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError
from os.path import expanduser, expandvars
+
+
class BravoConfigParser(SafeConfigParser):
"""
Extended ``ConfigParser``.
@@ -61,10 +63,8 @@ def read_configuration():
configuration = BravoConfigParser()
default_files = [
- "/etc/bravo/bravo.ini",
- expanduser("~/.bravo/bravo.ini"),
- expandvars("%APPDATA%/bravo/bravo.ini"),
- "bravo.ini",
+ expanduser("~/.lib2d/bravo.ini"),
+ "server.ini",
]
configuration.read(default_files)
View
6 lib2d/server/factory.py
@@ -7,7 +7,7 @@
from lib2d.server.protocol import Lib2dServerProtocol
-from packets import makePacket
+from lib2d.common.packets import make_packet
@@ -22,14 +22,14 @@ class Lib2dFactory(Factory):
timestamp = None
time = 0
- def __init__(self, name):
+ def __init__(self, config, name):
"""
Create factory and open a world
"""
self.name = name
+ self.config = config
self.world = None
- self.config = None
self.protocols = dict()
self.connectedIPs = defaultdict(int)
View
15 lib2d/server/protocol.py
@@ -1,7 +1,11 @@
from twisted.internet.protocol import Protocol
+from twisted.internet.task import cooperate, deferLater, LoopingCall
from twisted.internet import reactor
from twisted.protocols.policies import TimeoutMixin
+from lib2d.common.packets import make_packet, parse_packets
+from lib2d.common.temporal import timestamp_from_clock
+
# States of the protocol.
(STATE_UNAUTHENTICATED, STATE_CHALLENGED, STATE_AUTHENTICATED, STATE_LOCATED
) = range(4)
@@ -45,6 +49,17 @@ def __init__(self):
self.setTimeout(30)
+ self._ping_loop = LoopingCall(self.update_ping)
+
+
+ def update_ping(self):
+ """
+ Send a keepalive to the client.
+ """
+
+ timestamp = timestamp_from_clock(reactor)
+ self.write_packet("ping", pid=timestamp)
+
def ping(self, container):
now = timestamp_from_clock(reactor)
View
266 lib2d/server/rect.py
@@ -1,266 +0,0 @@
-def intersect(r1, r2):
- return (((r1.left >= r2.left and r1.left < r2.right) or
- (r2.left >= r1.left and r2.left < r1.right)) and
- ((r1.top >= r2.top and r1.top < r2.bottom) or
- (r2.top >= r1.top and r2.top < r1.bottom)))
-
-
-class Rect(object):
- """
- Pure Python Rect class that follows the PyGame API
-
- GIANT WARNING: This is completely in python and will be much slower than
- PyGame's built in Rect class. This rect should be used only
- if needed!
-
- These rects support floating point and are hashable.
- """
-
- __slots__ = ['_x', '_y', '_w', '_h']
-
- def __init__(self, *arg):
- """
- should accept rect like object or tuple of two tuples or one tuple
- of four numbers, store :x,y,h,w
- """
-
-
- print arg, len(arg)
- if isinstance(arg, Rect):
- self._x, self._y, self._w, self._h = arg
- elif isinstance(arg, list) or isinstance(arg, tuple):
- if len(arg) == 1:
- self._x, self._y, self._w, self._h = arg[0]
- elif len(arg) == 2:
- self._x, self._y = arg[0]
- self._w, self._h = arg[1]
- elif len(arg) == 4:
- self._x, self._y, self._w, self._h = arg
- elif hasattr(arg, 'rect'):
- self._x, self._y, self._w, self._h = arg.rect
- else:
- self._x, self._y, self._w, self._h = arg
-
-
- def __len__(self): return 4
-
-
- def __getitem__(self, key):
- if key == 0:
- return self._x
- elif key == 1:
- return self._y
- elif key == 2:
- return self._w
- elif key == 3:
- return self._h
- raise IndexError
-
-
- def copy(self):
- return Rect(self)
-
-
- def move(self, x, y):
- return Rect((self._x + x, self._y + y, self._w, self._h))
-
-
- def inflate(self, x, y):
- return Rect((self._x - x / 2, self._y - y / 2,
- self._w + x, self._h + y))
-
-
- def clamp(self):
- pass
-
-
- def clip(self, other):
- raise NotImplementedError
-
-
- def union(self, other):
- return Rect((min(self._x, other.left), min(self._y, other.top),
- max(self._w, other.right), max(self._h, other.height)))
-
-
- def unionall(self, *rects):
- rects.append(self)
- left = min([ r.left for r in rects ])
- top = min([ r.top for r in rects ])
- right = max([ r.right for r in rects ])
- bottom = max([ r.bottom for r in rects ])
- return Rect(left, top, right, bottom)
-
-
- def fit(self):
- raise NotImplementedError
-
-
- def normalize(self):
- x, y, w, h = self
-
- if self._w < 0:
- x += self._w
- w = -self._x
- if self._h < 0:
- y += self._h
- h = -self._y
-
- return Rect((x, y), (w, h))
-
-
- def contains(self, other):
- other = Rect(other)
- return ((self._x <= other.left) and
- (self._y <= other.top) and
- (self._x + self._w >= other.right) and
- (self._y + self._h >= other.bottom) and
- (self._x + self._w > other.left) and
- (self._y + self._h > other.top))
-
-
- def collidepoint(self, (x, y)):
- return x >= self._x and x < self._x + self._w and \
- y >= self._y and y < self._y + self._h
-
-
- def colliderect(self, other):
- return collide(self, Rect(other))
-
-
- def collidelist(self, rects):
- for i, rect in enumerate(rects):
- if intersect(self, r):
- return i
- return -1
-
-
- def collidelistall(self, l):
- return [ i for i, rect in enumerate(l) if intersect(self, Rect(rect)) ]
-
-
- def collidedict(self):
- raise NotImplementedError
-
-
- def collidedictall(self):
- raise NotImplementedError
-
-
- def as_rect(self):
- """
- pygame compatable format
- """
- print (self.topleft, self.size)
- return (self.topleft, self.size)
-
- @property
- def top(self):
- return self._y
-
-
- @property
- def left(self):
- return self._x
-
-
- @property
- def bottom(self):
- return self._y + self._h
-
-
- @property
- def right(self):
- return self._x + self._w
-
-
- @property
- def topleft(self):
- return self._x, self._y
-
-
- @property
- def bottomleft(self):
- return self._x, self._y + self._h
-
-
- @property
- def topright(self):
- return self._x + self._w, self._y
-
-
- @property
- def bottomright(self):
- return self._x + self._w, self._y + self._h
-
-
- @property
- def midtop(self):
- return self._x + self._w / 2, self._y
-
-
- @property
- def midleft(self):
- return self._x, self._y + self._h / 2
-
-
- @property
- def midbottom(self):
- return self._x + self._w / 2, self._y + self._h
-
-
- @property
- def midright(self):
- return self._x + self._w, self._y + self._h / 2
-
-
- @property
- def center(self):
- return self._x + self._w / 2, self.y + self._h / 2
-
-
- @property
- def centerx(self):
- return self._x + self._w / 2
-
-
- @property
- def centery(self):
- return self._y + self._h / 2
-
-
- @property
- def size(self):
- return self._w, self._h
-
-
- @property
- def width(self):
- return self._w
-
-
- @property
- def height(self):
- return self._h
-
-
- @property
- def w(self):
- return self._w
-
-
- @property
- def h(self):
- return self._h
-
-
- @property
- def x(self):
- return self._x
-
-
- @property
- def y(self):
- return self._y
-
-
View
2 lib2d/server/service.py
@@ -40,7 +40,7 @@ def removeService(self, service):
def configure_services(self):
for section in self.config.sections():
if section.startswith("world "):
- factory = BravoFactory(self.config, section[6:])
+ factory = Lib2dFactory(self.config, section[6:])
interfaces = self.config.getlist(section, "interfaces")
for service in services_for_endpoints(interfaces, factory):
View
61 server.ini
@@ -1,11 +1,6 @@
# Bravo sample configuration.
[bravo]
-# Try to enable Ampoule. This can massively improve server responsiveness, but
-# it can cause crashes, misrendering, and other weird bugs. You have been
-# warned.
-# DO NOT ENABLE ON WIN32. IT CAN CAUSE SERVER LOCKUPS.
-#ampoule = yes
ampoule = no
# Try to use the fancy console.
@@ -18,8 +13,7 @@ fancy_console = true
# The interfaces to listen on. All of the normal port rules apply; you probably
# cannot use ports below 1024 without root permissions.
# You may specify several interfaces by seperating them with a comma.
-#interfaces = tcp:25565, tcp:25566:interface=localhost
-interfaces = tcp:25565
+interfaces = tcp:25565, tcp:25566:interface=localhost
# The limitConnections is the number of total connections you want allowed on
# your server. Say you only want 30 players total, set limitConnections to 30.
@@ -42,21 +36,6 @@ limitPerIP = 0
#url = file://relative/path/to/world
url = file:///absolute/path/to/world
-# The gameplay mode for this server. Valid modes are "creative" and
-# "survival".
-mode = creative
-
-# Which seasons to enable for this world.
-# ~ winter: Spread snow over the world, and freeze bodies of water
-# ~ spring: Thaw everything
-seasons = winter, spring
-
-# Which serializer to use for saving worlds to disk.
-# ~ alpha: The Alpha NBT format
-# ~ beta: The Beta NBT/MCR format
-# Note: There is currently no automatic conversion from alpha to beta!
-serializer = beta
-
# Authenticator. There are only two options:
# ~ offline: anybody can log in, no authentication is done
# ~ online: only people logged into minecraft.net can log in
@@ -65,41 +44,3 @@ serializer = beta
#authenticator = online
authenticator = offline
-# This option enables a permanent cache of geometry, which persists regardless
-# of the number of clients connected to this world. It greatly speeds up login
-# at the cost of memory usage. This option will also cause the world to
-# generate geometry even when no clients are connected, to keep the cache
-# full. It is highly recommended to keep this cache enabled. The level of
-# caching done is dependent on the number used. 3 is the minimum required to
-# let clients login immediately with no glitches; 10 is the level of caching
-# done by Notchian servers. Anywhere between 3 and 10 is a good value. This
-# option does not increase overall RAM usage, just idle RAM usage!
-#
-# For the technically minded, the amount of additional RAM consumed by the
-# permanent cache is roughly (2s+1)^2 * c, where s is the size of the cache
-# according to this setting and c is the amount of RAM consumed by an
-# individual chunk, 80KiB or so. Thus, a few common settings and their RAM
-# usage:
-# ~ 3 -> 3 MiB
-# ~ 7 -> 17 MiB
-# ~ 8 -> 22 MiB
-# ~ 10 -> 34 MiB
-# ~ 20 -> 131 MiB
-perm_cache = 3
-
-# Plugins.
-# Bravo's plugin architecture is quite complex; if you're not sure how to
-# manage this section, read the documentation first to get things like the
-# names of plugins. While some examples are given, it's very important to read
-# the documentation in order to get a good grasp of how to customize your
-# server.
-
-# Plugin packs.
-# Adding the "beta" plugin pack is probably what most people want to do. It
-# provides most of the useful functionality found in Beta.
-packs = beta
-
-# The web service. Comment out to disable.
-[web]
-# Interfaces to listen on.
-interfaces = tcp:8080

0 comments on commit 02cbef2

Please sign in to comment.
Something went wrong with that request. Please try again.