1 /**
2 Copyright: Copyright (c) 2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module clientapp;
8 
9 import std.datetime;
10 import std.stdio;
11 import std.conv : to;
12 
13 
14 import derelict.enet.enet;
15 
16 import anchovy.gui;
17 import anchovy.graphics.windows.glfwwindow;
18 import anchovy.gui.application.application;
19 import anchovy.gui.databinding.list;
20 
21 import client;
22 import connection;
23 import packets;
24 
25 class MessageInputBehavior : EditBehavior
26 {
27 	void delegate() onEnter;
28 
29 	override bool keyPressed(Widget widget, KeyPressEvent event)
30 	{
31 		if (event.keyCode == KeyCode.KEY_ENTER)
32 		{
33 			if (onEnter) onEnter();
34 			return true;
35 		}
36 		else
37 		{
38 			return super.keyPressed(widget, event);
39 		}
40 	}
41 }
42 
43 class ChatClientApp : Application!GlfwWindow
44 {
45 	Client client;
46 	SimpleList!dstring messageList;
47 	Widget msgInput;
48 	Widget connectButton;
49 	MessageInputBehavior messageInput;
50 
51 	this(uvec2 windowSize, string caption)
52 	{
53 		super(windowSize, caption);
54 	}
55 
56 	override void update(double dt)
57 	{
58 		if (client) client.update(0);
59 
60 		fpsHelper.update(dt);
61 		timerManager.updateTimers(window.elapsedTime);
62 		context.update(dt);
63 	}
64 
65 	override void load(in string[] args)
66 	{
67 		renderer.setClearColor(Color(255,255,255));
68 		messageList = new SimpleList!dstring;
69 
70 		context.behaviorFactories["messageInput"] ~= delegate IWidgetBehavior (){return new MessageInputBehavior;};
71 
72 		templateManager.parseFile("chat.sdl");
73 
74 		auto mainLayer = context.createWidget("mainLayer");
75 		context.addRoot(mainLayer);
76 
77 		msgInput = context.getWidgetById("messsageInput");
78 		messageInput = msgInput.getWidgetBehavior!MessageInputBehavior();
79 		messageInput.onEnter = &onEnter;
80 
81 		connectButton = context.getWidgetById("connect");
82 		connectButton.addEventHandler(&onConnect);
83 		context.getWidgetById("messages").setProperty!("list", List!dstring)(messageList);
84 	}
85 
86 	override void unload()
87 	{
88 		if (client) client.stop();
89 	}
90 
91 	void printfln(Args...)(string fmt, Args args)
92 	{
93 		writefln(fmt, args);
94 		messageList.push(format(fmt, args).to!dstring);
95 	}
96 
97 	bool onConnect(Widget widget, PointerClickEvent event)
98 	{
99 		if (client && client.isRunning)
100 		{
101 			doDisconnect();
102 			return true;
103 		}
104 
105 		string address = context.getWidgetById("ip")["text"].coerce!string;
106 		ushort port = context.getWidgetById("port")["text"].coerce!ushort;
107 		string name = context.getWidgetById("nick")["text"].coerce!string;
108 
109 		if (!client)
110 		{
111 			client = new Client();
112 			client.registerPacketHandler!SessionInfoPacket(&handleSessionInfoPacket);
113 			client.registerPacketHandler!UserLoggedInPacket(&handleUserLoggedInPacket);
114 			client.registerPacketHandler!UserLoggedOutPacket(&handleUserLoggedOutPacket);
115 			client.registerPacketHandler!MessagePacket(&handleMessagePacket);
116 
117 			client.disconnectHandler = &onDisconnect;
118 
119 			client.start();
120 		}
121 
122 		client.connect(name, address, port);
123 
124 		connectButton["text"] = "Disconnect";
125 
126 		return true;
127 	}
128 
129 	void doDisconnect()
130 	{
131 		client.disconnnect();
132 	}
133 
134 	void onDisconnect()
135 	{
136 		printfln("Disconnected from server");
137 		connectButton["text"] = "Connect";
138 	}
139 
140 	void onEnter()
141 	{
142 		if (client && client.isRunning)
143 		{
144 			if (messageInput.text.length > 0)
145 			{
146 				writefln("enter");
147 				sendMessage(messageInput.text.to!string);
148 				messageInput.text = null;
149 			}
150 		}
151 	}
152 
153 	void sendMessage(string msg)
154 	{
155 		ubyte[] packet = client.createPacket(MessagePacket(0, msg));
156 		client.send(packet);
157 	}
158 
159 	void handleSessionInfoPacket(ubyte[] packetData, UserId peer)
160 	{
161 		SessionInfoPacket loginInfo = client.unpackPacket!SessionInfoPacket(packetData);
162 
163 		client.userNames = loginInfo.userNames;
164 		client.myId = loginInfo.yourId;
165 
166 		writefln("my id is %s", client.myId);
167 	}
168 
169 	void handleUserLoggedInPacket(ubyte[] packetData, UserId peer)
170 	{
171 		UserLoggedInPacket newUser = client.unpackPacket!UserLoggedInPacket(packetData);
172 		client.userNames[newUser.userId] = newUser.userName;
173 		printfln("%s has connected", newUser.userName);
174 	}
175 
176 	void handleUserLoggedOutPacket(ubyte[] packetData, UserId peer)
177 	{
178 		UserLoggedOutPacket packet = client.unpackPacket!UserLoggedOutPacket(packetData);
179 		printfln("%s has disconnected", client.userName(packet.userId));
180 		client.userNames.remove(packet.userId);
181 	}
182 
183 	void handleMessagePacket(ubyte[] packetData, UserId peer)
184 	{
185 		MessagePacket msg = client.unpackPacket!MessagePacket(packetData);
186 		if (msg.userId == 0)
187 			printfln("%s", msg.msg);
188 		else
189 			printfln("%s> %s", client.userName(msg.userId), msg.msg);
190 	}
191 
192 	override void closePressed()
193 	{
194 		isRunning = false;
195 	}
196 }
197 
198 version(linux)
199 {
200 	pragma(lib, "dl");
201 }
202 
203 void main(string[] args)
204 {
205 	DerelictENet.load();
206 
207 	int err = enet_initialize();
208 
209 	if (err != 0)
210 	{
211 		writefln("Error loading ENet library");
212 		return;
213 	}
214 	else
215 	{
216 		ENetVersion ever = enet_linked_version();
217 		writefln("Loaded ENet library v%s.%s.%s",
218 			ENET_VERSION_GET_MAJOR(ever),
219 			ENET_VERSION_GET_MINOR(ever),
220 			ENET_VERSION_GET_PATCH(ever));
221 	}
222 
223 	auto app = new ChatClientApp(uvec2(450, 500), "Chat");
224 	app.run(args);
225 }