import threading import time from utils import CHUNK_WIDTH, CHUNK_HEIGHT, Position class ChunkDiff(): """ Represents differences between two chunks (changes to be made to a chunk). Can be used to transform a chunk into another chunk. Todo: Implement delete diff """ def __init__(self): self._chars = {} @classmethod def from_dict(cls, d): diff = cls() diff._chars = d return diff #self._chars = d.copy() @classmethod def from_string(cls, s): diff = cls() #for c in string pass def copy(self): return ChunkDiff.from_dict(self.to_dict().copy()) def combine(self, diff): newdiff = self.copy() newdiff.apply(diff) return newdiff def to_dict(self): return self._chars #return self._chars.copy() def set(self, x, y, character): pos = x+y*CHUNK_WIDTH self._chars[pos] = character def delete(self, x, y): self.set(x, y, " ") def clear_deletions(self): self._chars = {i: v for i, v in self._chars.items() if v != " "} def apply(self, diff): for i, c in diff._chars.items(): self._chars[i] = c def lines(self): d = self._chars s = "".join(d.get(i, " ") for i in range(CHUNK_WIDTH*CHUNK_HEIGHT)) return [s[i:i+CHUNK_WIDTH] for i in range(0, CHUNK_WIDTH*CHUNK_HEIGHT, CHUNK_WIDTH)] def empty(self): return not bool(self._chars) class Chunk(): """ Represents a chunk (16x8 characters on the map). Is able to generate diffs - from another chunk - from direct changes - from accumulated changes """ def __init__(self): self._content = ChunkDiff() self._modifications = ChunkDiff() self.touch() def set(self, x, y, character): self._modifications.set(x, y, character) self.touch() def delete(self, x, y): self._modifications.delete(x, y) self.touch() def commit_changes(self): self.commit_diff(self._modifications) self._modifications = ChunkDiff() def commit_diff(self, diff): self._content.apply(diff) self._content.clear_deletions() self.touch() def drop_changes(self): self._modifications = ChunkDiff() def get_changes(self): return self._modifications 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(): window.addstr(y, x, line) y += 1 def modified(self): return not self._modifications.empty() def empty(self): return self._content.empty() and self._modifications.empty() class ChunkPool(): """ Is a collection of chunks. Allows user to manage (get, modify, delete) chunks, keeps track of chunks for them. Load chunks it doesn't know. """ def __init__(self): self._chunks = {} self._lock = threading.RLock() def __enter__(self): self._lock.acquire() return self def __exit__(self, type, value, tb): self._lock.release() def set(self, pos, chunk): self._chunks[pos] = chunk def get(self, pos): return self._chunks.get(pos) def create(self, pos): chunk = Chunk() self.set(pos, 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): changes = [] for pos, chunk in self._chunks.items(): changes.append((pos, chunk.get_changes())) chunk.commit_changes() return changes def save_changes(self): self.commit_changes() def load(self, pos): if not pos in self._chunks: self.create(pos) def load_list(self, coords): for pos in coords: self.load(pos) def unload(self, pos): if pos in self._chunks: del self._chunks[pos] def unload_list(self, coords): for pos in coords: self.unload(pos) def clean_up(self, except_for=[], condition=lambda pos, chunk: True): # 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)] #self.save_changes() # needs to be accounted for by the user coords = [] for pos, chunk in self._chunks.items(): if not pos in except_for and condition(pos, chunk): coords.append(pos) self.unload_list(coords)