/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * JavaWorld Library, Copyright 2011 Bryan Chadwick * * * * FILE: ./universe/base/UniverseBase.java * * * * This file is part of JavaWorld. * * * * JavaWorld is free software: you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation, either version * * 3 of the License, or (at your option) any later version. * * * * JavaWorld is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with JavaWorld. If not, see <http://www.gnu.org/licenses/>. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package universe.base; import universe.control.*; import java.awt.Color; import java.awt.RenderingHints; import universe.*; import javax.swing.*; import java.awt.Graphics2D; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.Serializable; import image.*; /* TODO: */ /** A Class representing a Universe (a collection of Worlds) of some type, and related methods * and Function Objects for handling messages and events */ public class UniverseBase<Msg extends Serializable>{ public static int PORT = 4567; private Universe<Msg> initial; double time; // Handlers OnDraw<Msg> ondraw; OnTick<Msg> ontick; OnNew<Msg> onnew; OnDisconnect<Msg> ondisconnect; OnMsg<Msg> onmsg; public UniverseBase(Universe<Msg> initial){ this(initial, 0.05, null, null, null, null, null); } public UniverseBase<Msg> onDraw(OnDraw<Msg> ondraw){ return new UniverseBase<Msg>(this.initial, this.time, ondraw, this.ontick, this.onnew, this.ondisconnect, this.onmsg); } public UniverseBase<Msg> onTick(OnTick<Msg> ontick){ return onTick(ontick, 0.05); } public UniverseBase<Msg> onTick(OnTick<Msg> ontick, double time){ return new UniverseBase<Msg>(this.initial, this.time, this.ondraw, ontick, this.onnew, this.ondisconnect, this.onmsg); } public UniverseBase<Msg> onNew(OnNew<Msg> onnew){ return new UniverseBase<Msg>(this.initial, this.time, this.ondraw, this.ontick, onnew, this.ondisconnect, this.onmsg); } public UniverseBase<Msg> onDisconnect(OnDisconnect<Msg> ondisconnect){ return new UniverseBase<Msg>(this.initial, this.time, this.ondraw, this.ontick, this.onnew, ondisconnect, this.onmsg); } public UniverseBase<Msg> onMsg(OnMsg<Msg> onmsg){ return new UniverseBase<Msg>(this.initial, this.time, this.ondraw, this.ontick, this.onnew, this.ondisconnect, onmsg); } private static int SPACE = 5; public Universe<Msg> universe(){ if(this.onmsg == null) throw new RuntimeException("No Universe Message Handler"); if(this.ondraw == null) throw new RuntimeException("No Universe Draw Handler"); JDialog f = new JDialog((JFrame)null, "Universe", true); Scene scn = doOnDraw(this.initial); final Handler<Msg> handler = new Handler<Msg>(this, this.initial, new BufferedImage((int)(scn.width()+2*SPACE), (int)(scn.height()+2*SPACE), BufferedImage.TYPE_INT_RGB), (int)(this.time*1000), f); f.setSize((int)(SPACE*2+Math.max(20, 14+scn.width())), (int)(Math.max(20, SPACE*2+31+scn.height()))); f.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); f.setResizable(false); f.getContentPane().add(handler); handler.run.start(); try{ final Server<Msg> server = new Server<Msg>(PORT, false, this); Thread msgs = new Thread(){ public void run(){ while(true){ try{ synchronized(server){ server.wait(); } if(server.hasMessage()){ // Handle messages... WithWorld m = server.nextMessage(); Bundle<Msg> res = null; if(m.isConnect()){ res = doOnNew(handler.u, m.getWImp()); }else if(m.isDisconnect()){ res = doDisconnect(handler.u, m.getWImp(), server); }else if(m.isTransfer()){ res = doOnMsg(handler.u, m.getWImp(), m.<Msg>payload()); }else Server.p("Unknown Message Type"); if(res != null){ handler.replace(res); for(Mail<Msg> mail : res.getMails()){ server.processMail(mail, m.getWImp()); } // res.getWorlds() } } }catch(InterruptedException e){ Server.p("!!Interrupted!!"); } } } }; msgs.start(); f.setVisible(true); // Kill all the threads System.exit(0); //handler.run.stop(); }catch(java.io.IOException e){ throw new RuntimeException(e); } return handler.u; } // Private constructors... private UniverseBase(Universe<Msg> init, double time, OnDraw<Msg> ondraw, OnTick<Msg> ontick, OnNew<Msg> onnew, OnDisconnect<Msg> ondisconnect, OnMsg<Msg> onmsg){ this.initial = init; this.time = time; this.ondraw = ondraw; this.ontick = ontick; this.onnew = onnew; this.ondisconnect = ondisconnect; this.onmsg = onmsg; } /** Handles the nitty-gritty of Universe updates */ static class Handler<Msg extends Serializable> extends javax.swing.JComponent implements ActionListener{ private static final long serialVersionUID = 1L; UniverseBase<Msg> univ; Universe<Msg> u; BufferedImage buffer; Graphics2D graph; Timer run; Handler(UniverseBase<Msg> univ, Universe<Msg> u, BufferedImage buff, int msec, JDialog dia){ this.univ = univ; this.u = u; this.run = new Timer(msec, this); this.buffer = buff; this.graph = buff.createGraphics(); this.graph.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); this.graph.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); } public void actionPerformed(ActionEvent e){ replace(this.univ.doOnTick(this.u)); } private synchronized void replace(Bundle<Msg> b){ if(b == null)return; boolean change = !this.u.equals(b.getUniverse()); this.u = b.getUniverse(); if(change)repaint(); } public void paint(java.awt.Graphics g){ this.graph.setColor(Color.white); this.graph.fillRect(0,0, this.getWidth(), this.getHeight()); this.graph.clipRect(SPACE, SPACE, this.buffer.getWidth()-SPACE*2, this.buffer.getHeight()-SPACE*2); this.univ.doOnDraw(this.u).paint(this.graph,SPACE,SPACE); g.drawImage(this.buffer, 0, 0, null); } } Scene doOnDraw(Universe<Msg> u){ return this.ondraw.apply(u); } Bundle<Msg> doOnTick(Universe<Msg> u){ if(this.ontick == null)return null; return this.ontick.apply(u); } Bundle<Msg> doOnNew(Universe<Msg> u, IWorld w){ if(this.onnew == null)return null; return this.onnew.apply(u, w); } Bundle<Msg> doDisconnect(Universe<Msg> u, IWorld w, Server<Msg> server){ if(this.ondisconnect == null)return null; server.removeWorld(w); return this.ondisconnect.apply(u, w); } Bundle<Msg> doOnMsg(Universe<Msg> u, IWorld w, Msg msg){ if(this.onmsg == null)return null; return this.onmsg.apply(u, w, msg); } }