Implement parts of clientchunkpool and fix softlock in the process

This commit is contained in:
Joscha 2017-04-09 09:52:11 +00:00
parent c5af7c2480
commit 23da1ea5d9
5 changed files with 90 additions and 31 deletions

View file

@ -1,6 +1,6 @@
import threading import threading
import time import time
from utils import CHUNK_WIDTH, CHUNK_HEIGHT from utils import CHUNK_WIDTH, CHUNK_HEIGHT, Position
class ChunkDiff(): class ChunkDiff():
""" """
@ -134,14 +134,28 @@ class ChunkPool():
def __exit__(self, type, value, tb): def __exit__(self, type, value, tb):
self._lock.release() self._lock.release()
def set(self, pos, chunk):
self._chunks[pos] = chunk
def get(self, pos): def get(self, pos):
return self._chunks.get(pos) return self._chunks.get(pos)
def create(self, pos): def create(self, pos):
chunk = Chunk() chunk = Chunk()
self._chunks[pos] = chunk self.set(pos, chunk)
return chunk return chunk
def apply_changes(self, changes):
for change in changes:
pos = Position(change[0][0], change[0][1])
diff = change[1]
chunk = self.get(pos)
if not chunk:
chunk = self.create(pos)
chunk.commit_diff(diff)
def commit_changes(self): def commit_changes(self):
changes = [] changes = []
@ -174,6 +188,8 @@ class ChunkPool():
# old list comprehension which became too long: # old list comprehension which became too long:
#coords = [pos for pos, chunk in self._chunks.items() if not pos in except_for and condition(chunk)] #coords = [pos for pos, chunk in self._chunks.items() if not pos in except_for and condition(chunk)]
self.save_changes()
coords = [] coords = []
for pos, chunk in self._chunks.items(): for pos, chunk in self._chunks.items():

View file

@ -1,20 +1,20 @@
import curses import curses
import os
import string import string
import sys import sys
import threading import threading
from maps import Map, ChunkMap from maps import Map, ChunkMap
from chunks import ChunkDiff
from clientchunkpool import ClientChunkPool from clientchunkpool import ClientChunkPool
# import fron chunks, maps, clientchunkpool
class Client(): class Client():
def __init__(self, address): def __init__(self, address):
self.stopping = False self.stopping = False
self.chunkmap_active = False self.chunkmap_active = False
self.address = address self.address = address
self.drawevent = threading.Event() self._drawevent = threading.Event()
self.pool = ClientChunkPool() self.pool = ClientChunkPool(self)
#self.map_ = Map(sizex, sizey, self.pool) #self.map_ = Map(sizex, sizey, self.pool)
#self.chunkmap = Chunkmap(sizex, sizey, self.pool) # size changeable by +/-? #self.chunkmap = Chunkmap(sizex, sizey, self.pool) # size changeable by +/-?
@ -22,7 +22,7 @@ class Client():
def launch(self, stdscr): def launch(self, stdscr):
sizey, sizex = stdscr.getmaxyx() sizey, sizex = stdscr.getmaxyx()
self.map_ = Map(sizex, sizey, self.pool, self.drawevent) self.map_ = Map(sizex, sizey, self.pool, self)
self.chunkmap = ChunkMap(self.map_) self.chunkmap = ChunkMap(self.map_)
# start input thread # start input thread
@ -35,7 +35,8 @@ class Client():
self.inputthread.start() self.inputthread.start()
while not self.stopping: while not self.stopping:
self.drawevent.wait() self._drawevent.wait()
self._drawevent.clear()
stdscr.noutrefresh() stdscr.noutrefresh()
with self.map_ as m: with self.map_ as m:
m.draw() m.draw()
@ -45,12 +46,14 @@ class Client():
curses.curs_set(False) curses.curs_set(False)
else: else:
curses.curs_set(True) curses.curs_set(True)
#m.update_cursor() #m.update_cursor()
#m.noutrefresh() #m.noutrefresh()
curses.doupdate() curses.doupdate()
self.drawevent.clear()
def redraw(self):
self._drawevent.set()
def input_thread(self, scr): def input_thread(self, scr):
while True: while True:
@ -59,7 +62,7 @@ class Client():
if i == "\x1b": self.stop() if i == "\x1b": self.stop()
elif i == "KEY_F(2)": elif i == "KEY_F(2)":
self.chunkmap_active = not self.chunkmap_active self.chunkmap_active = not self.chunkmap_active
self.drawevent.set() self.redraw()
elif i == "KEY_F(5)": self.map_.redraw() elif i == "KEY_F(5)": self.map_.redraw()
# scrolling the map (10 vertical, 20 horizontal) # scrolling the map (10 vertical, 20 horizontal)
elif i == "kUP5": self.map_.scroll(0, -10) elif i == "kUP5": self.map_.scroll(0, -10)
@ -86,11 +89,23 @@ class Client():
elif i == "\x7f": self.map_.delete() elif i == "\x7f": self.map_.delete()
elif i == "\n": self.map_.newline() elif i == "\n": self.map_.newline()
else: sys.stdout.write(repr(i) + "\n") else: sys.stderr.write(repr(i) + "\n")
def stop(self): def stop(self):
self.stopping = True self.stopping = True
self.drawevent.set() self.redraw()
def request_chunks(self, coords):
def execute():
changes = [(pos, ChunkDiff()) for pos in coords]
with self.pool as pool:
pool.apply_changes(changes)
tx = threading.Timer(1, execute)
tx.start()
def send_changes(self, changes):
pass
def main(argv): def main(argv):
if len(argv) != 2: if len(argv) != 2:
@ -98,6 +113,8 @@ def main(argv):
print(" {} address".format(argv[0])) print(" {} address".format(argv[0]))
return return
os.environ.setdefault('ESCDELAY', '25') # only a 25 millisecond delay
client = Client(argv[1]) client = Client(argv[1])
curses.wrapper(client.launch) curses.wrapper(client.launch)

View file

@ -5,8 +5,31 @@ class ClientChunkPool(ChunkPool):
A ChunkPool that requests/loads chunks from a client. A ChunkPool that requests/loads chunks from a client.
""" """
def __init__(self): def __init__(self, client):
super().__init__() super().__init__()
self.max_age = 10 #s
self._client = client
pass #def commit_changes(self):
#changes = []
#for pos, chunk in self._chunks.items():
#changes.append((pos, chunk.get_changes()))
#chunk.commit_changes()
#return changes
def apply_changes(self, changes):
super().apply_changes(changes)
self._client.redraw()
def save_changes(self):
changes = self.commit_changes()
self._client.send_changes(changes)
def load(self, pos):
raise Exception
def load_list(self, coords):
self._client.request_chunks(coords)

23
maps.py
View file

@ -4,15 +4,13 @@ import math
import threading import threading
from utils import CHUNK_HEIGHT, CHUNK_WIDTH, chunkx, chunky, inchunkx, inchunky, Position from utils import CHUNK_HEIGHT, CHUNK_WIDTH, chunkx, chunky, inchunkx, inchunky, Position
import sys
class Map(): class Map():
""" """
A map which displays chunks and a cursor on the screen. A map which displays chunks and a cursor on the screen.
Allows for user to modify chunks in an intuitive way. Allows for user to modify chunks in an intuitive way.
""" """
def __init__(self, width, height, chunkpool, drawevent): def __init__(self, width, height, chunkpool, client):
self._lock = threading.RLock() self._lock = threading.RLock()
self.chunkpreload = 0 # preload chunks in this radius (they will count as "visible") self.chunkpreload = 0 # preload chunks in this radius (they will count as "visible")
@ -26,7 +24,7 @@ class Map():
self.lastcury = self.cursory self.lastcury = self.cursory
self.chunkpool = chunkpool self.chunkpool = chunkpool
self.drawevent = drawevent self.client = client
self._pad = curses.newpad(5, 5) self._pad = curses.newpad(5, 5)
self.resize(width, height) self.resize(width, height)
@ -44,7 +42,7 @@ class Map():
def redraw(self): def redraw(self):
self._pad.redrawwin() self._pad.redrawwin()
self.drawevent.set() self.client.redraw()
def draw(self): def draw(self):
with self.chunkpool as pool: with self.chunkpool as pool:
@ -93,7 +91,7 @@ class Map():
pool.load_list(coords) pool.load_list(coords)
pool.clean_up(except_for=coords, condition=self._unload_condition) pool.clean_up(except_for=coords, condition=self._unload_condition)
self.drawevent.set() self.client.redraw()
def resize(self, width, height): def resize(self, width, height):
self.width = width self.width = width
@ -208,7 +206,7 @@ class ChunkMap():
styles = { styles = {
"empty": ChunkStyle("()", 2), "empty": ChunkStyle("()", 2),
"normal": ChunkStyle("[]", 3), "normal": ChunkStyle("[]", 3),
"old": ChunkStyle("{}", 4), "unload": ChunkStyle("{}", 4),
"visible": ChunkStyle("##", 5), "visible": ChunkStyle("##", 5),
"modified": ChunkStyle("!!", 6), "modified": ChunkStyle("!!", 6),
} }
@ -225,7 +223,7 @@ class ChunkMap():
if curses.has_colors(): if curses.has_colors():
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_BLUE) # empty chunk curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_BLUE) # empty chunk
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE) # chunk curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE) # chunk
curses.init_pair(4, curses.COLOR_BLACK, curses.COLOR_YELLOW) # old chunk curses.init_pair(4, curses.COLOR_BLACK, curses.COLOR_YELLOW) # chunk to be unloaded
curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_GREEN) # visible chunk curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_GREEN) # visible chunk
curses.init_pair(6, curses.COLOR_BLACK, curses.COLOR_RED) # modified chunk curses.init_pair(6, curses.COLOR_BLACK, curses.COLOR_RED) # modified chunk
@ -236,7 +234,10 @@ class ChunkMap():
def draw(self): def draw(self):
with self.chunkpool as pool: with self.chunkpool as pool:
minx, maxx, miny, maxy = self.get_min_max(pool) if pool._chunks:
minx, maxx, miny, maxy = self.get_min_max(pool)
else:
minx, maxx, miny, maxy = 0, 0, 0, 0
sizex = maxx - minx sizex = maxx - minx
sizey = maxy - miny sizey = maxy - miny
self.update_size(sizex, sizey) self.update_size(sizex, sizey)
@ -281,8 +282,8 @@ class ChunkMap():
if pos in self.map_.visible_chunk_coords(): if pos in self.map_.visible_chunk_coords():
return "visible" return "visible"
if chunk.age() > self.chunkpool.max_age: if self.map_._unload_condition(pos, chunk):
return "old" return "unload"
if chunk.empty(): if chunk.empty():
return "empty" return "empty"

View file

@ -2,8 +2,10 @@ from collections import namedtuple
Position = namedtuple("Position", "x y") Position = namedtuple("Position", "x y")
CHUNK_WIDTH = 64 #CHUNK_WIDTH = 64
CHUNK_HEIGHT = 32 #CHUNK_HEIGHT = 32
CHUNK_WIDTH = 32
CHUNK_HEIGHT = 16
def chunkx(value): def chunkx(value):
return value//CHUNK_WIDTH return value//CHUNK_WIDTH