diff --git a/chunks.py b/chunks.py index fcf9740..4208027 100644 --- a/chunks.py +++ b/chunks.py @@ -58,7 +58,7 @@ class ChunkDiff(): return [s[i:i+CHUNK_WIDTH] for i in range(0, CHUNK_WIDTH*CHUNK_HEIGHT, CHUNK_WIDTH)] def empty(self): - return bool(self._chars) + return not bool(self._chars) class Chunk(): @@ -96,8 +96,11 @@ class Chunk(): def get_changes(self): return self._modifications - def touch(self): - self.last_modified = time.time() + def touch(self, now=None): + self.last_modified = now or time.time() + + def age(self, now=None): + return self.last_modified - (now or time.time()) def draw_to(self, x, y, window): for line in self._content.combine(self._modifications).lines(): @@ -136,9 +139,21 @@ class ChunkPool(): if not pos in self._chunks: self.create(pos) + def unload(self, pos): + if pos in self._chunks: + del self._chunks[pos] + def load_list(self, coords): for pos in coords: self.load(pos) + def unload_list(self, coords): + for pos in coords: + self.unload(pos) + + def clean_up(self, except_for=[]): + coords = [pos for pos in self._chunks if not pos in except_for] + self.unload_list(coords) + def get(self, pos): return self._chunks.get(pos) diff --git a/client.py b/client.py index 581b8f8..5c23ca7 100644 --- a/client.py +++ b/client.py @@ -2,18 +2,19 @@ import curses import string import sys import threading -from maps import Map -from chunks import ChunkPool +from maps import Map, ChunkMap +from clientchunkpool import ClientChunkPool # import fron chunks, maps, clientchunkpool class Client(): def __init__(self, address): self.stopping = False + self.chunkmap_active = False self.address = address self.drawevent = threading.Event() - self.pool = ChunkPool() + self.pool = ClientChunkPool() #self.map_ = Map(sizex, sizey, self.pool) #self.chunkmap = Chunkmap(sizex, sizey, self.pool) # size changeable by +/-? @@ -22,6 +23,7 @@ class Client(): def launch(self, stdscr): sizey, sizex = stdscr.getmaxyx() self.map_ = Map(sizex, sizey, self.pool, self.drawevent) + self.chunkmap = ChunkMap(self.map_) # start input thread self.inputthread = threading.Thread( @@ -37,6 +39,16 @@ class Client(): stdscr.noutrefresh() with self.map_ as m: m.draw() + + if self.chunkmap_active: + self.chunkmap.draw() + curses.curs_set(False) + else: + curses.curs_set(True) + + #m.update_cursor() + #m.noutrefresh() + curses.doupdate() self.drawevent.clear() @@ -45,7 +57,10 @@ class Client(): i = scr.getkey() if i == "\x1b": self.stop() - #elif i == "r": self.map_.redraw() + elif i == "KEY_F(2)": + self.chunkmap_active = not self.chunkmap_active + self.drawevent.set() + elif i == "KEY_F(5)": self.map_.redraw() # normal cursor movement elif i == "KEY_UP": self.map_.move_cursor(0, -1) elif i == "KEY_DOWN": self.map_.move_cursor(0, 1) diff --git a/clientchunkpool.py b/clientchunkpool.py index cde947a..c67cff9 100644 --- a/clientchunkpool.py +++ b/clientchunkpool.py @@ -1,8 +1,12 @@ -from .chunks.py import ChunkPool +from chunks import ChunkPool class ClientChunkPool(ChunkPool): """ A ChunkPool that requests/loads chunks from a client. """ + def __init__(self): + super().__init__() + self.max_age = 10 #s + pass diff --git a/maps.py b/maps.py index 9a7b88d..4f44126 100644 --- a/maps.py +++ b/maps.py @@ -1,3 +1,4 @@ +from collections import namedtuple import curses import math import threading @@ -28,6 +29,9 @@ class Map(): self._pad = curses.newpad(5, 5) self.resize(width, height) + + if curses.has_colors(): + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) # chunk not loaded def __enter__(self): self._lock.acquire() @@ -51,13 +55,17 @@ class Map(): else: self.draw_empty_to(x*CHUNK_WIDTH, y*CHUNK_HEIGHT) - # set cursor position in world + self.update_cursor() + self.noutrefresh() + + def noutrefresh(self): + self._pad.noutrefresh(inchunky(self.worldy), inchunkx(self.worldx), 0, 0, self.height-1, self.width-1) + + def update_cursor(self): self._pad.move( self.cursory - chunky(self.worldy)*CHUNK_HEIGHT, self.cursorx - chunkx(self.worldx)*CHUNK_WIDTH ) - - self._pad.noutrefresh(inchunky(self.worldy), inchunkx(self.worldx), 0, 0, self.height-1, self.width-1) def draw_empty_to(self, x, y): if curses.has_colors(): @@ -71,7 +79,9 @@ class Map(): def load_visible(self): with self.chunkpool as pool: - pool.load_list(self.visible_chunk_coords()) + coords = self.visible_chunk_coords() + pool.load_list(coords) + #pool.clean_up(except_for=coords) self.drawevent.set() @@ -103,12 +113,11 @@ class Map(): def write(self, char): with self.chunkpool as pool: chunk = pool.get(Position(chunkx(self.cursorx), chunky(self.cursory))) - if not chunk: - chunk = pool.create(Position(chunkx(self.cursorx), chunky(self.cursory))) - chunk.set(inchunkx(self.cursorx), inchunky(self.cursory), char) - - self.move_cursor(1, 0, False) + if chunk: + chunk.set(inchunkx(self.cursorx), inchunky(self.cursory), char) + + self.move_cursor(1, 0, False) def delete(self): with self.chunkpool as pool: @@ -177,7 +186,8 @@ class Map(): #) #self.load_visible() - + +ChunkStyle = namedtuple("ChunkStyle", "string color") class ChunkMap(): """ @@ -185,17 +195,93 @@ class ChunkMap(): Might show additional details too (i.e. if a chunk has been modified). """ - def __init__(self, chunkpool): - self.cpool = chunkpool -# -# def draw(self): -# pass -# -# def resize(self, size): -# pass -# - def move(self, x, y, corner): - pass + styles = { + "empty": ChunkStyle("()", 2), + "normal": ChunkStyle("[]", 3), + "old": ChunkStyle("{}", 4), + "visible": ChunkStyle("##", 5), + "modified": ChunkStyle("!!", 6), + } - def toggle(self): - pass + def __init__(self, map_): + self.map_ = map_ + self.chunkpool = map_.chunkpool + self.corner = "ur" # upper right + + #minx, maxx, miny, maxy = self.get_min_max() + #self.win = curses.newwin(maxy-miny+2, maxx-minx+2) + self.win = curses.newwin(2, 2) + + if curses.has_colors(): + 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(4, curses.COLOR_BLACK, curses.COLOR_YELLOW) # old 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 + + def update_size(self, sizex, sizey): + winy, winx = self.win.getmaxyx() + if winx != 2*sizex + 4 or winy != sizey + 3: + self.win.resize(sizey + 3, 2*sizex + 4) + sys.stderr.write("{}\n".format(self.win.getmaxyx())) + + def draw(self): + with self.chunkpool as pool: + minx, maxx, miny, maxy = self.get_min_max(pool) + sizex = maxx - minx + sizey = maxy - miny + sys.stderr.write("{} {} {} {} | {} {}\n".format(minx, maxx, miny, maxy, sizex, sizey)) + self.update_size(sizex, sizey) + + self.win.erase() + self.win.border() + + for pos, chunk in pool._chunks.items(): + tp = self.type_of(pos, chunk) + sys.stderr.write(tp + "\n") + sys.stderr.write("{} {}\n".format(pos.x - minx + 1, pos.y - miny + 1)) + if curses.has_colors(): + self.win.addstr( + pos.y - miny + 1, + 2*(pos.x - minx) + 1, + " ", + curses.color_pair(self.styles[tp].color) + ) + else: + self.win.addstr( + pos.y - miny + 1, + 2*(pos.x - minx) + 1, + self.styles[tp].string + ) + + self.win.noutrefresh() + + def get_min_max(self, pool): + minx = min(pos.x for pos in pool._chunks) + maxx = max(pos.x for pos in pool._chunks) + miny = min(pos.y for pos in pool._chunks) + maxy = max(pos.y for pos in pool._chunks) + + return minx, maxx, miny, maxy + + def get_size(self): + minx, maxx, miny, maxy = self.get_min_max() + return maxx - minx, maxy - miny + + def type_of(self, pos, chunk): + if chunk.modified(): + return "modified" + + if pos in self.map_.visible_chunk_coords(): + return "visible" + + if chunk.age() > self.chunkpool.max_age: + return "old" + + if chunk.empty(): + return "empty" + + return "normal" + + #def move(self, x, y, corner): + #pass diff --git a/utils.py b/utils.py index 8083e21..5c5b1cc 100644 --- a/utils.py +++ b/utils.py @@ -2,8 +2,8 @@ from collections import namedtuple Position = namedtuple("Position", "x y") -CHUNK_WIDTH = 16 -CHUNK_HEIGHT = 8 +CHUNK_WIDTH = 64 +CHUNK_HEIGHT = 32 def chunkx(value): return value//CHUNK_WIDTH