15784
|
1 |
package net.ercatec.hw2ircsvr;
|
|
2 |
|
|
3 |
import net.ercatec.hw.INetClient;
|
|
4 |
import net.ercatec.hw.ProtocolConnection;
|
|
5 |
|
|
6 |
import java.io.BufferedReader;
|
|
7 |
import java.io.InputStream;
|
|
8 |
import java.io.InputStreamReader;
|
|
9 |
import java.io.OutputStream;
|
|
10 |
import java.net.InetSocketAddress;
|
|
11 |
import java.net.ServerSocket;
|
|
12 |
import java.net.Socket;
|
|
13 |
import java.util.ArrayList;
|
|
14 |
import java.util.Arrays;
|
|
15 |
import java.util.HashMap;
|
|
16 |
import java.util.Hashtable;
|
|
17 |
import java.util.List;
|
|
18 |
import java.util.Map;
|
|
19 |
import java.util.Queue;
|
|
20 |
import java.util.concurrent.LinkedBlockingQueue;
|
|
21 |
import java.util.Collections;
|
|
22 |
import java.util.Vector;
|
|
23 |
|
|
24 |
import java.util.regex.Pattern;
|
|
25 |
import java.util.regex.PatternSyntaxException;
|
|
26 |
|
|
27 |
import java.lang.IllegalArgumentException;
|
|
28 |
|
|
29 |
// for auth files
|
|
30 |
import java.util.Properties;
|
|
31 |
import java.io.FileInputStream;
|
|
32 |
import java.io.IOException;
|
|
33 |
|
|
34 |
/* TODO
|
|
35 |
* disconnect clients that are not irc clients
|
|
36 |
* disconnect excess flooders
|
|
37 |
* recognizes stuff after : as single arg
|
|
38 |
* collect pre-irc-join messages and show on join
|
|
39 |
* allow negating regexps
|
|
40 |
* ban
|
|
41 |
* banlist
|
|
42 |
* commandquery // wth did I mean by that?
|
|
43 |
* more room info
|
|
44 |
* filter rooms
|
|
45 |
* warnings
|
|
46 |
* global notice
|
|
47 |
*/
|
|
48 |
|
|
49 |
/**
|
|
50 |
* @author sheepluva
|
|
51 |
*
|
|
52 |
* based on jircs by Alexander Boyd
|
|
53 |
*/
|
|
54 |
public class Connection implements INetClient, Runnable
|
|
55 |
{
|
|
56 |
private static final String DESCRIPTION_SHORT
|
|
57 |
= "connect to hedgewars via irc!";
|
|
58 |
|
|
59 |
private static final String VERSION = "0.6.7-Alpha_2015-11-07";
|
|
60 |
|
|
61 |
|
|
62 |
private static final String MAGIC_BYTES = "[\1\2\3]";
|
|
63 |
private static final char MAGIC_BYTE_ACTION = ((char)1); // ^A
|
|
64 |
private static final char MAGIC_BYTE_BOLD = ((char)2); // ^B
|
|
65 |
private static final char MAGIC_BYTE_COLOR = ((char)3); // ^C
|
|
66 |
|
|
67 |
private static final String[] DEFAULT_MOTD = {
|
|
68 |
" ",
|
|
69 |
" "+MAGIC_BYTE_COLOR+"06"+MAGIC_BYTE_BOLD+" SUCH FLUFFY!",
|
|
70 |
" ",
|
|
71 |
" "+MAGIC_BYTE_COLOR+"04 MUCH BAH "+MAGIC_BYTE_COLOR+"00__ _ ",
|
|
72 |
" "+MAGIC_BYTE_COLOR+"00 .-.' `; `-."+MAGIC_BYTE_COLOR+"00_ __ _ ",
|
|
73 |
" "+MAGIC_BYTE_COLOR+"00 (_, .-:' `; `"+MAGIC_BYTE_COLOR+"00-._ ",
|
|
74 |
" "+MAGIC_BYTE_COLOR+"14 ,'"+MAGIC_BYTE_COLOR+"02o "+MAGIC_BYTE_COLOR+"00( (_, ) ",
|
|
75 |
" "+MAGIC_BYTE_COLOR+"14 (__"+MAGIC_BYTE_COLOR+"00,-' "+MAGIC_BYTE_COLOR+"15,'"+MAGIC_BYTE_COLOR+"12o "+MAGIC_BYTE_COLOR+"00( )> ",
|
|
76 |
" "+MAGIC_BYTE_COLOR+"00 ( "+MAGIC_BYTE_COLOR+"15(__"+MAGIC_BYTE_COLOR+"00,-' ) ",
|
|
77 |
" "+MAGIC_BYTE_COLOR+"00 `-'._.--._( ) ",
|
|
78 |
" "+MAGIC_BYTE_COLOR+"14 ||| |||"+MAGIC_BYTE_COLOR+"00`-'._.--._.-' ",
|
|
79 |
" "+MAGIC_BYTE_COLOR+"15 ||| ||| ",
|
|
80 |
" "+MAGIC_BYTE_COLOR+"07"+MAGIC_BYTE_BOLD+" WOW! ",
|
|
81 |
" "+MAGIC_BYTE_COLOR+"09 VERY SHEEP ",
|
|
82 |
" ",
|
|
83 |
" ",
|
|
84 |
" ",
|
|
85 |
" "+MAGIC_BYTE_COLOR+"4 Latest hw2irc crimes/changes:",
|
|
86 |
" ping: ping of hwserver will only get reply if irc client pingable",
|
|
87 |
" ping: pings of irc clients will only get reply if hwserver pingable",
|
|
88 |
" rooms: id-rotation, make channel names at least 2 digits wide",
|
|
89 |
" auth: support passhash being loaded local auth file and irc pass (sent as cleartext - DO NOT USE!)",
|
|
90 |
" ",
|
|
91 |
" ",
|
|
92 |
};
|
|
93 |
|
|
94 |
|
|
95 |
private static final String DEFAULT_QUIT_REASON = "User quit";
|
|
96 |
// NOT final
|
|
97 |
private static char CHAT_COMMAND_CHAR = '\\';
|
|
98 |
|
|
99 |
private final class Room {
|
|
100 |
public final int id;
|
|
101 |
public final String chan;
|
|
102 |
public String name;
|
|
103 |
private String owner = "";
|
|
104 |
public int nPlayers = 0;
|
|
105 |
public int nTeams = 0;
|
|
106 |
|
|
107 |
public Room(final int id, final String name, final String owner) {
|
|
108 |
this.id = id;
|
|
109 |
this.chan = (id<10?"#0":"#") + id;
|
|
110 |
this.name = name;
|
|
111 |
this.setOwner(owner);
|
|
112 |
}
|
|
113 |
|
|
114 |
public String getOwner() { return this.owner; }
|
|
115 |
|
|
116 |
public void setOwner(final String owner) {
|
|
117 |
// don't to this for first owner
|
|
118 |
if (!this.owner.isEmpty()) {
|
|
119 |
|
|
120 |
// owner didn't change
|
|
121 |
if (this.owner.equals(owner))
|
|
122 |
return;
|
|
123 |
|
|
124 |
// update old room owner
|
|
125 |
final Player oldOwner = allPlayers.get(this.owner);
|
|
126 |
|
|
127 |
if (oldOwner != null)
|
|
128 |
oldOwner.isRoomAdm = false;
|
|
129 |
|
|
130 |
}
|
|
131 |
|
|
132 |
// update new room owner
|
|
133 |
final Player newOwner = allPlayers.get(owner);
|
|
134 |
|
|
135 |
if (newOwner != null)
|
|
136 |
newOwner.isRoomAdm = true;
|
|
137 |
|
|
138 |
this.owner = owner;
|
|
139 |
|
|
140 |
}
|
|
141 |
}
|
|
142 |
|
|
143 |
private final class Player {
|
|
144 |
public final String nick;
|
|
145 |
public final String ircNick;
|
|
146 |
private boolean isAdm;
|
|
147 |
private boolean isCont;
|
|
148 |
private boolean isReg;
|
|
149 |
public boolean inRoom;
|
|
150 |
public boolean isRoomAdm;
|
|
151 |
private String ircId;
|
|
152 |
private String ircHostname;
|
|
153 |
private boolean announced;
|
|
154 |
|
|
155 |
// server info
|
|
156 |
private String version = "";
|
|
157 |
private String ip = "";
|
|
158 |
private String room = "";
|
|
159 |
|
|
160 |
public Player(final String nick) {
|
|
161 |
this.nick = nick;
|
|
162 |
this.ircNick = hwToIrcNick(nick);
|
|
163 |
this.announced = false;
|
|
164 |
updateIrcHostname();
|
|
165 |
}
|
|
166 |
|
|
167 |
public String getIrcHostname() { return ircHostname; }
|
|
168 |
public String getIrcId() { return ircId; }
|
|
169 |
|
|
170 |
public String getRoom() {
|
|
171 |
if (room.isEmpty())
|
|
172 |
return room;
|
|
173 |
|
|
174 |
return "[" + ((isAdm?"@":"") + (isRoomAdm?"+":"") + this.room);
|
|
175 |
}
|
|
176 |
|
|
177 |
public boolean needsAnnounce() {
|
|
178 |
return !announced;
|
|
179 |
}
|
|
180 |
|
|
181 |
public void setAnnounced() {
|
|
182 |
announced = true;
|
|
183 |
}
|
|
184 |
|
|
185 |
public void setInfo(final String ip, final String version, final String room) {
|
|
186 |
if (this.version.isEmpty()) {
|
|
187 |
this.version = version;
|
|
188 |
this.ip = ip.replaceAll("^\\[|]$", "");
|
|
189 |
updateIrcHostname();
|
|
190 |
}
|
|
191 |
|
|
192 |
if (room.isEmpty())
|
|
193 |
this.room = room;
|
|
194 |
else
|
|
195 |
this.room = room.replaceAll("^\\[[@+]*", "");
|
|
196 |
}
|
|
197 |
|
|
198 |
public boolean isServerAdmin() { return isAdm; }
|
|
199 |
//public boolean isContributor() { return isCont; }
|
|
200 |
public boolean isRegistered() { return isReg; }
|
|
201 |
|
|
202 |
public void setServerAdmin(boolean isAdm) {
|
|
203 |
this.isAdm = isAdm; updateIrcHostname(); }
|
|
204 |
public void setContributor(boolean isCont) {
|
|
205 |
this.isCont = isCont; updateIrcHostname(); }
|
|
206 |
public void setRegistered(boolean isReg) {
|
|
207 |
this.isReg = isReg; updateIrcHostname(); }
|
|
208 |
|
|
209 |
private void updateIrcHostname() {
|
|
210 |
ircHostname = ip.isEmpty()?"":(ip + '/');
|
|
211 |
ircHostname += "hw/";
|
|
212 |
if (!version.isEmpty())
|
|
213 |
ircHostname += version;
|
|
214 |
if (isAdm)
|
|
215 |
ircHostname += "/admin";
|
|
216 |
else if (isCont)
|
|
217 |
ircHostname += "/contributor";
|
|
218 |
else if (isReg)
|
|
219 |
ircHostname += "/member";
|
|
220 |
else
|
|
221 |
ircHostname += "/player";
|
|
222 |
|
|
223 |
updateIrcId();
|
|
224 |
}
|
|
225 |
|
|
226 |
private void updateIrcId() {
|
|
227 |
ircId = ircNick + "!~" + ircNick + "@" + ircHostname;
|
|
228 |
}
|
|
229 |
}
|
|
230 |
|
|
231 |
public String hw404NickToIrcId(String nick) {
|
|
232 |
nick = hwToIrcNick(nick);
|
|
233 |
return nick + "!~" + nick + "@hw/404";
|
|
234 |
}
|
|
235 |
|
|
236 |
// hash tables are thread-safe
|
|
237 |
private final Map<String, Player> allPlayers = new Hashtable<String, Player>();
|
|
238 |
private final Map<String, Player> roomPlayers = new Hashtable<String, Player>();
|
|
239 |
private final Map<Integer, Room> roomsById = new Hashtable<Integer, Room>();
|
|
240 |
private final Map<String, Room> roomsByName = new Hashtable<String, Room>();
|
|
241 |
private final List<Room> roomsSorted = new Vector<Room>();
|
|
242 |
|
|
243 |
private final List<String> ircPingQueue = new Vector<String>();
|
|
244 |
|
|
245 |
private static final String DEFAULT_SERVER_HOST = "netserver.hedgewars.org";
|
|
246 |
private static String SERVER_HOST = DEFAULT_SERVER_HOST;
|
|
247 |
private static int IRC_PORT = 46667;
|
|
248 |
|
|
249 |
private String hostname;
|
|
250 |
|
|
251 |
private static final String LOBBY_CHANNEL_NAME = "#lobby";
|
|
252 |
private static final String ROOM_CHANNEL_NAME = "#room";
|
|
253 |
|
|
254 |
// hack
|
|
255 |
// TODO: ,
|
|
256 |
private static final char MAGIC_SPACE = ' ';
|
|
257 |
private static final char MAGIC_ATSIGN = '៙';
|
|
258 |
private static final char MAGIC_PERCENT = '%';
|
|
259 |
private static final char MAGIC_PLUS = '+';
|
|
260 |
private static final char MAGIC_EXCLAM = '❢';
|
|
261 |
private static final char MAGIC_COMMA = ',';
|
|
262 |
private static final char MAGIC_COLON = ':';
|
|
263 |
|
|
264 |
private static String hwToIrcNick(final String nick) {
|
|
265 |
return nick
|
|
266 |
.replace(' ', MAGIC_SPACE)
|
|
267 |
.replace('@', MAGIC_ATSIGN)
|
|
268 |
.replace('%', MAGIC_PERCENT)
|
|
269 |
.replace('+', MAGIC_PLUS)
|
|
270 |
.replace('!', MAGIC_EXCLAM)
|
|
271 |
.replace(',', MAGIC_COMMA)
|
|
272 |
.replace(':', MAGIC_COLON)
|
|
273 |
;
|
|
274 |
}
|
|
275 |
private static String ircToHwNick(final String nick) {
|
|
276 |
return nick
|
|
277 |
.replace(MAGIC_COLON, ':')
|
|
278 |
.replace(MAGIC_COMMA, ',')
|
|
279 |
.replace(MAGIC_EXCLAM, '!')
|
|
280 |
.replace(MAGIC_PLUS, '+')
|
|
281 |
.replace(MAGIC_PERCENT, '%')
|
|
282 |
.replace(MAGIC_ATSIGN, '@')
|
|
283 |
.replace(MAGIC_SPACE, ' ')
|
|
284 |
;
|
|
285 |
}
|
|
286 |
|
|
287 |
private ProtocolConnection hwcon;
|
|
288 |
private boolean joined = false;
|
|
289 |
private boolean ircJoined = false;
|
|
290 |
|
|
291 |
private void collectFurtherInfo() {
|
|
292 |
hwcon.sendPing();
|
|
293 |
hwcon.processNextClientFlagsMessages();
|
|
294 |
}
|
|
295 |
|
|
296 |
public void onPing() {
|
|
297 |
send("PING :" + globalServerName);
|
|
298 |
}
|
|
299 |
|
|
300 |
public void onPong() {
|
|
301 |
if (!ircPingQueue.isEmpty())
|
|
302 |
send(":" + globalServerName + " PONG " + globalServerName
|
|
303 |
+ " :" + ircPingQueue.remove(0));
|
|
304 |
|
|
305 |
}
|
|
306 |
|
|
307 |
public void onConnectionLoss() {
|
|
308 |
quit("Connection Loss");
|
|
309 |
}
|
|
310 |
|
|
311 |
public void onDisconnect(final String reason) {
|
|
312 |
quit(reason);
|
|
313 |
}
|
|
314 |
|
|
315 |
public String onPasswordHashNeededForAuth() {
|
|
316 |
return passwordHash;
|
|
317 |
}
|
|
318 |
|
|
319 |
public void onMalformedMessage(String contents)
|
|
320 |
{
|
|
321 |
this.logError("MALFORMED MESSAGE: " + contents);
|
|
322 |
}
|
|
323 |
|
|
324 |
public void onChat(final String user, final String message) {
|
|
325 |
String ircId;
|
|
326 |
Player player = allPlayers.get(user);
|
|
327 |
if (player == null) {
|
|
328 |
// fake user - so probably a notice
|
|
329 |
sendChannelNotice(message, hwToIrcNick(user));
|
|
330 |
//logWarning("onChat(): Couldn't find player with specified nick! nick: " + user);
|
|
331 |
//send(":" + hw404NickToIrcId(user) + " PRIVMSG "
|
|
332 |
//+ LOBBY_CHANNEL_NAME + " :" + hwActionToIrc(message));
|
|
333 |
}
|
|
334 |
else
|
|
335 |
send(":" + player.getIrcId() + " PRIVMSG "
|
|
336 |
+ LOBBY_CHANNEL_NAME + " :" + hwActionToIrc(message));
|
|
337 |
}
|
|
338 |
|
|
339 |
public void onWelcomeMessage(final String message) {
|
|
340 |
}
|
|
341 |
|
|
342 |
public void onNotice(int number) {
|
|
343 |
}
|
|
344 |
|
|
345 |
public void onBanListEntry(BanType type, String target, String duration, String reason) {
|
|
346 |
// TODO
|
|
347 |
}
|
|
348 |
public void onBanListEnd() {
|
|
349 |
// TODO
|
|
350 |
}
|
|
351 |
|
|
352 |
public String onNickCollision(final String nick) {
|
|
353 |
return nick + "_";
|
|
354 |
}
|
|
355 |
|
|
356 |
public void onNickSet(final String nick) {
|
|
357 |
final String newNick = hwToIrcNick(nick);
|
|
358 |
// tell irc client
|
|
359 |
send(":" + ownIrcNick + "!~" + username + "@"
|
|
360 |
+ hostname + " NICK :" + nick);
|
|
361 |
ownIrcNick = newNick;
|
|
362 |
updateLogPrefix();
|
|
363 |
logInfo("Nickname set to " + nick);
|
|
364 |
}
|
|
365 |
|
|
366 |
private void flagAsInLobby(final Player player) {
|
|
367 |
if (!ircJoined)
|
|
368 |
return;
|
|
369 |
final String ircNick = player.ircNick;
|
|
370 |
if (player.isServerAdmin())
|
|
371 |
send(":room-part!~@~ MODE " + LOBBY_CHANNEL_NAME + " -h+o " + ircNick + " " + ircNick);
|
|
372 |
//else
|
|
373 |
//send(":room-part!~@~ MODE " + LOBBY_CHANNEL_NAME + " +v " + ircNick);
|
|
374 |
}
|
|
375 |
|
|
376 |
private void flagAsInRoom(final Player player) {
|
|
377 |
if (!ircJoined)
|
|
378 |
return;
|
|
379 |
final String ircNick = player.ircNick;
|
|
380 |
if (player.isServerAdmin())
|
|
381 |
send(":room-join!~@~ MODE " + LOBBY_CHANNEL_NAME + " -o+h " + ircNick + " " + ircNick);
|
|
382 |
//else
|
|
383 |
//send(":room-join!~@~ MODE " + LOBBY_CHANNEL_NAME + " -v " + ircNick);
|
|
384 |
}
|
|
385 |
|
|
386 |
// TODO somewhere: escape char for magic chars!
|
|
387 |
|
|
388 |
// TODO /join with playername => FOLLOW :D
|
|
389 |
|
|
390 |
public void sendPlayerMode(final Player player) {
|
|
391 |
char c;
|
|
392 |
if (player.isServerAdmin())
|
|
393 |
c = player.inRoom?'h':'o';
|
|
394 |
else if (player.isRegistered())
|
|
395 |
c = 'v';
|
|
396 |
else
|
|
397 |
// no mode
|
|
398 |
return;
|
|
399 |
|
|
400 |
send(":server-join!~@~ MODE " + LOBBY_CHANNEL_NAME + " +" + c + " " + player.ircNick);
|
|
401 |
}
|
|
402 |
|
|
403 |
private Player ownPlayer = null;
|
|
404 |
|
|
405 |
public void onLobbyJoin(final String[] users) {
|
|
406 |
|
|
407 |
final List<Player> newPlayers = new ArrayList<Player>(users.length);
|
|
408 |
|
|
409 |
// process joins
|
|
410 |
for (final String user : users) {
|
|
411 |
final Player player = new Player(user);
|
|
412 |
if (ownPlayer == null)
|
|
413 |
ownPlayer = player;
|
|
414 |
newPlayers.add(player);
|
|
415 |
allPlayers.put(user, player);
|
|
416 |
}
|
|
417 |
|
|
418 |
// make sure we get the client flags before we announce anything
|
|
419 |
collectFurtherInfo();
|
|
420 |
|
|
421 |
// get player info
|
|
422 |
// NOTE: if player is in room, then info was already retrieved
|
|
423 |
for (final Player player : newPlayers) {
|
|
424 |
if (!player.inRoom)
|
|
425 |
hwcon.requestInfo(player.nick);
|
|
426 |
}
|
|
427 |
|
|
428 |
/* DISABLED - we'll announce later - when receiving info
|
|
429 |
// announce joins
|
|
430 |
if (ircJoined) {
|
|
431 |
for (final Player player : newPlayers) {
|
|
432 |
final String ircId = player.getIrcId();
|
|
433 |
send(":" + ircId
|
|
434 |
+ " JOIN "+ lobbyChannel.name);
|
|
435 |
sendPlayerMode(player);
|
|
436 |
}
|
|
437 |
}
|
|
438 |
*/
|
|
439 |
if (!ircJoined) {
|
|
440 |
// don't announced players that were there before join already
|
|
441 |
for (final Player player : newPlayers) {
|
|
442 |
player.setAnnounced();
|
|
443 |
}
|
|
444 |
}
|
|
445 |
|
|
446 |
if (!joined) {
|
|
447 |
joined = true;
|
|
448 |
// forget password hash, we don't need it anymore.
|
|
449 |
passwordHash = "";
|
|
450 |
logInfo("Hedgewars server/lobby joined.");
|
|
451 |
sendSelfNotice("Hedgewars server was joined successfully");
|
|
452 |
// do this after join so that rooms can be assigned to their owners
|
|
453 |
hwcon.requestRoomsList();
|
|
454 |
}
|
|
455 |
}
|
|
456 |
|
|
457 |
private void makeIrcJoinLobby() {
|
|
458 |
sendGlobal("INVITE " + ownIrcNick + " " + LOBBY_CHANNEL_NAME);
|
|
459 |
try{Thread.sleep(3000);}catch(Exception e){}
|
|
460 |
join(lobbyChannel.name);
|
|
461 |
sendSelfNotice("Joining lobby-channel: " + lobbyChannel.name);
|
|
462 |
}
|
|
463 |
|
|
464 |
private void announcePlayerJoinLobby(final Player player) {
|
|
465 |
player.setAnnounced();
|
|
466 |
send(":" + player.getIrcId()
|
|
467 |
+ " JOIN "+ lobbyChannel.name);
|
|
468 |
sendPlayerMode(player);
|
|
469 |
}
|
|
470 |
|
|
471 |
public void onLobbyLeave(final String user, final String reason) {
|
|
472 |
final Player player = allPlayers.get(user);
|
|
473 |
if (player == null) {
|
|
474 |
logWarning("onLobbyLeave(): Couldn't find player with specified nick! nick: " + user);
|
|
475 |
sendIfJoined(":" + hw404NickToIrcId(user)
|
|
476 |
+ " PART " + lobbyChannel.name + " " + reason);
|
|
477 |
}
|
|
478 |
else {
|
|
479 |
if (ircJoined && player.needsAnnounce())
|
|
480 |
announcePlayerJoinLobby(player);
|
|
481 |
sendIfJoined(":" + player.getIrcId()
|
|
482 |
+ " PART " + lobbyChannel.name + " " + reason);
|
|
483 |
allPlayers.remove(user);
|
|
484 |
}
|
|
485 |
}
|
|
486 |
|
|
487 |
private int lastRoomId = 0;
|
|
488 |
|
|
489 |
public void onRoomInfo(final String name, final String flags,
|
|
490 |
final String newName, final int nUsers,
|
|
491 |
final int nTeams, final String owner,
|
|
492 |
final String map, final String style,
|
|
493 |
final String scheme, final String weapons) {
|
|
494 |
|
|
495 |
Room room = roomsByName.get(name);
|
|
496 |
|
|
497 |
if (room == null) {
|
|
498 |
// try to reuse old ids
|
|
499 |
if (lastRoomId >= 90)
|
|
500 |
lastRoomId = 9;
|
|
501 |
|
|
502 |
// search for first free
|
|
503 |
while(roomsById.containsKey(++lastRoomId)) { }
|
|
504 |
|
|
505 |
room = new Room(lastRoomId, newName, owner);
|
|
506 |
roomsById.put(lastRoomId, room);
|
|
507 |
roomsByName.put(newName, room);
|
|
508 |
roomsSorted.add(room);
|
|
509 |
}
|
|
510 |
else if (!room.name.equals(newName)) {
|
|
511 |
room.name = newName;
|
|
512 |
roomsByName.put(newName, roomsByName.remove(name));
|
|
513 |
}
|
|
514 |
|
|
515 |
// update data
|
|
516 |
room.setOwner(owner);
|
|
517 |
room.nPlayers = nUsers;
|
|
518 |
room.nTeams = nTeams;
|
|
519 |
}
|
|
520 |
|
|
521 |
public void onRoomDel(final String name) {
|
|
522 |
final Room room = roomsByName.remove(name);
|
|
523 |
|
|
524 |
if (room != null) {
|
|
525 |
roomsById.remove(room.id);
|
|
526 |
roomsSorted.remove(room);
|
|
527 |
}
|
|
528 |
}
|
|
529 |
|
|
530 |
public void onRoomJoin(final String[] users) {
|
|
531 |
}
|
|
532 |
|
|
533 |
public void onRoomLeave(final String[] users) {
|
|
534 |
}
|
|
535 |
|
|
536 |
// TODO vector that remembers who's info was requested for manually
|
|
537 |
List<String> requestedInfos = new Vector<String>();
|
|
538 |
|
|
539 |
public void onUserInfo(final String user, final String ip, final String version, final String room) {
|
|
540 |
Player player = allPlayers.get(user);
|
|
541 |
if (player != null) {
|
|
542 |
player.setInfo(ip, version, room);
|
|
543 |
if (ircJoined) {
|
|
544 |
if (player.needsAnnounce())
|
|
545 |
announcePlayerJoinLobby(player);
|
|
546 |
}
|
|
547 |
else {
|
|
548 |
if (player == ownPlayer) {
|
|
549 |
|
|
550 |
makeIrcJoinLobby();
|
|
551 |
}
|
|
552 |
}
|
|
553 |
}
|
|
554 |
|
|
555 |
// if MANUAL send notice
|
|
556 |
if (requestedInfos.remove(user)) {
|
|
557 |
final String nick = hwToIrcNick(user);
|
|
558 |
sendServerNotice(nick + " - " + buildInfoString(ip, version, room));
|
|
559 |
}
|
|
560 |
}
|
|
561 |
|
|
562 |
public void onUserFlagChange(final String user, final UserFlagType flag, final boolean newValue) {
|
|
563 |
final Player player = allPlayers.get(user);
|
|
564 |
if (player == null) {
|
|
565 |
logError("onUserFlagChange(): Couldn't find player with specified nick! nick: " + user);
|
|
566 |
return;
|
|
567 |
}
|
|
568 |
switch (flag) {
|
|
569 |
case ADMIN:
|
|
570 |
player.setServerAdmin(newValue);
|
|
571 |
if (newValue) {
|
|
572 |
logDebug(user + " is server admin");
|
|
573 |
//sendIfJoined(":server!~@~ MODE " + LOBBY_CHANNEL_NAME + " -v+o " + player.ircNick + " " + player.ircNick);
|
|
574 |
}
|
|
575 |
break;
|
|
576 |
case INROOM:
|
|
577 |
player.inRoom = newValue;
|
|
578 |
if (newValue) {
|
|
579 |
flagAsInRoom(player);
|
|
580 |
logDebug(user + " entered a room");
|
|
581 |
// get new room info
|
|
582 |
hwcon.requestInfo(player.nick);
|
|
583 |
}
|
|
584 |
else {
|
|
585 |
flagAsInLobby(player);
|
|
586 |
logDebug(user + " returned to lobby");
|
|
587 |
player.inRoom = false;
|
|
588 |
}
|
|
589 |
break;
|
|
590 |
case REGISTERED:
|
|
591 |
player.setRegistered(newValue);
|
|
592 |
break;
|
|
593 |
default: break;
|
|
594 |
}
|
|
595 |
}
|
|
596 |
|
|
597 |
public class Channel
|
|
598 |
{
|
|
599 |
private String topic;
|
|
600 |
private final String name;
|
|
601 |
private final Map<String, Player> players;
|
|
602 |
|
|
603 |
public Channel(final String name, final String topic, final Map<String, Player> players) {
|
|
604 |
this.name = name;
|
|
605 |
this.topic = topic;
|
|
606 |
this.players = players;
|
|
607 |
}
|
|
608 |
}
|
|
609 |
|
|
610 |
public void logInfo(final String message) {
|
|
611 |
System.out.println(this.logPrefix + ": " + message);
|
|
612 |
}
|
|
613 |
|
|
614 |
public void logDebug(final String message) {
|
|
615 |
System.out.println(this.logPrefix + "| " + message);
|
|
616 |
}
|
|
617 |
|
|
618 |
public void logWarning(final String message) {
|
|
619 |
System.err.println(this.logPrefix + "? " + message);
|
|
620 |
}
|
|
621 |
|
|
622 |
public void logError(final String message) {
|
|
623 |
System.err.println(this.logPrefix + "! " + message);
|
|
624 |
}
|
|
625 |
|
|
626 |
|
|
627 |
//private static final Object mutex = new Object();
|
|
628 |
private boolean joinSent = false;
|
|
629 |
private Socket socket;
|
|
630 |
private String username;
|
|
631 |
private String ownIrcNick;
|
|
632 |
private String description;
|
|
633 |
private static Map<String, Connection> connectionMap = new HashMap<String, Connection>();
|
|
634 |
// TODO those MUST NOT be static!
|
|
635 |
//private Map<String, Channel> channelMap = new HashMap<String, Channel>();
|
|
636 |
private final Channel lobbyChannel;
|
|
637 |
private static String globalServerName;
|
|
638 |
private String logPrefix;
|
|
639 |
private final String clientId;
|
|
640 |
private String passwordHash = "";
|
|
641 |
|
|
642 |
private final Connection thisConnection;
|
|
643 |
|
|
644 |
public Connection(Socket socket, final String clientId) throws Exception
|
|
645 |
{
|
|
646 |
this.ownIrcNick = "NONAME";
|
|
647 |
this.socket = socket;
|
|
648 |
this.hostname = ((InetSocketAddress)socket.getRemoteSocketAddress())
|
|
649 |
.getAddress().getHostAddress();
|
|
650 |
this.clientId = clientId;
|
|
651 |
updateLogPrefix();
|
|
652 |
thisConnection = this;
|
|
653 |
logInfo("New Connection");
|
|
654 |
|
|
655 |
this.hwcon = null;
|
|
656 |
|
|
657 |
try {
|
|
658 |
this.hwcon = new ProtocolConnection(this, SERVER_HOST);
|
|
659 |
logInfo("Connection to " + SERVER_HOST + " established.");
|
|
660 |
}
|
|
661 |
catch(Exception ex) {
|
|
662 |
final String errmsg = "Could not connect to " + SERVER_HOST + ": "
|
|
663 |
+ ex.getMessage();
|
|
664 |
logError(errmsg);
|
|
665 |
sendQuit(errmsg);
|
|
666 |
}
|
|
667 |
|
|
668 |
final String lobbyTopic = " # " + SERVER_HOST + " - HEDGEWARS SERVER LOBBY # ";
|
|
669 |
this.lobbyChannel = new Channel(LOBBY_CHANNEL_NAME, lobbyTopic, allPlayers);
|
|
670 |
|
|
671 |
// start in new thread
|
|
672 |
if (hwcon != null) {
|
|
673 |
(this.hwcon.processMessages(true)).start();
|
|
674 |
}
|
|
675 |
}
|
|
676 |
|
|
677 |
private void updateLogPrefix() {
|
|
678 |
if (ownIrcNick == null)
|
|
679 |
this.logPrefix = clientId + " ";
|
|
680 |
else
|
|
681 |
this.logPrefix = clientId + " [" + ownIrcNick + "] ";
|
|
682 |
}
|
|
683 |
|
|
684 |
private void setNick(final String nick) {
|
|
685 |
if (passwordHash.isEmpty()) {
|
|
686 |
try {
|
|
687 |
final Properties authProps = new Properties();
|
|
688 |
final String authFile = this.hostname + ".auth";
|
|
689 |
logInfo("Attempting to load auth info from " + authFile);
|
|
690 |
authProps.load(new FileInputStream(authFile));
|
|
691 |
passwordHash = authProps.getProperty(nick, "");
|
|
692 |
if (passwordHash.isEmpty())
|
|
693 |
logInfo("Auth info file didn't contain any password hash for: " + nick);
|
|
694 |
} catch (IOException e) {
|
|
695 |
logInfo("Auth info file couldn't be loaded.");
|
|
696 |
}
|
|
697 |
}
|
|
698 |
|
|
699 |
// append _ just in case
|
|
700 |
if (!passwordHash.isEmpty() || nick.endsWith("_")) {
|
|
701 |
ownIrcNick = nick;
|
|
702 |
hwcon.setNick(ircToHwNick(nick));
|
|
703 |
}
|
|
704 |
else {
|
|
705 |
final String nick_ = nick + "_";
|
|
706 |
ownIrcNick = nick_;
|
|
707 |
hwcon.setNick(ircToHwNick(nick_));
|
|
708 |
}
|
|
709 |
}
|
|
710 |
|
|
711 |
public String getRepresentation()
|
|
712 |
{
|
|
713 |
return ownIrcNick + "!~" + username + "@" + hostname;
|
|
714 |
}
|
|
715 |
|
|
716 |
private static int lastClientId = 0;
|
|
717 |
|
|
718 |
/**
|
|
719 |
* @param args
|
|
720 |
*/
|
|
721 |
public static void main(String[] args) throws Throwable
|
|
722 |
{
|
|
723 |
if (args.length > 0)
|
|
724 |
{
|
|
725 |
SERVER_HOST = args[0];
|
|
726 |
}
|
|
727 |
if (args.length > 1)
|
|
728 |
{
|
|
729 |
IRC_PORT = Integer.parseInt(args[1]);
|
|
730 |
}
|
|
731 |
|
|
732 |
globalServerName = "hw2irc";
|
|
733 |
|
|
734 |
if (!SERVER_HOST.equals(DEFAULT_SERVER_HOST))
|
|
735 |
globalServerName += "~" + SERVER_HOST;
|
|
736 |
|
|
737 |
final int port = IRC_PORT;
|
|
738 |
ServerSocket ss = new ServerSocket(port);
|
|
739 |
System.out.println("Listening on port " + port);
|
|
740 |
while (true)
|
|
741 |
{
|
|
742 |
Socket s = ss.accept();
|
|
743 |
final String clientId = "client" + (++lastClientId) + '-'
|
|
744 |
+ ((InetSocketAddress)s.getRemoteSocketAddress())
|
|
745 |
.getAddress().getHostAddress();
|
|
746 |
try {
|
|
747 |
Connection clientCon = new Connection(s, clientId);
|
|
748 |
//clientCon.run();
|
|
749 |
Thread clientThread = new Thread(clientCon, clientId);
|
|
750 |
clientThread.start();
|
|
751 |
}
|
|
752 |
catch (Exception ex) {
|
|
753 |
System.err.println("FATAL: Server connection thread " + clientId + " crashed on startup! " + ex.getMessage());
|
|
754 |
ex.printStackTrace();
|
|
755 |
}
|
|
756 |
|
|
757 |
System.out.println("Note: Not accepting new clients for the next " + SLEEP_BETWEEN_LOGIN_DURATION + "s, trying to avoid reconnecting too quickly.");
|
|
758 |
Thread.sleep(SLEEP_BETWEEN_LOGIN_DURATION * 1000);
|
|
759 |
System.out.println("Note: Accepting clients again!");
|
|
760 |
}
|
|
761 |
}
|
|
762 |
|
|
763 |
private static final int SLEEP_BETWEEN_LOGIN_DURATION = 122;
|
|
764 |
|
|
765 |
private boolean hasQuit = false;
|
|
766 |
|
|
767 |
public synchronized void quit(final String reason) {
|
|
768 |
if (hasQuit)
|
|
769 |
return;
|
|
770 |
|
|
771 |
hasQuit = true;
|
|
772 |
// disconnect from hedgewars server
|
|
773 |
if (hwcon != null)
|
|
774 |
hwcon.disconnect(reason);
|
|
775 |
// disconnect irc client
|
|
776 |
sendQuit("Quit: " + reason);
|
|
777 |
// wait some time so that last data can be pushed
|
|
778 |
try {
|
|
779 |
Thread.sleep(200);
|
|
780 |
}
|
|
781 |
catch (Exception e) { }
|
|
782 |
// terminate
|
|
783 |
terminateConnection = true;
|
|
784 |
}
|
|
785 |
|
|
786 |
|
|
787 |
private static String hwActionToIrc(final String chatMsg) {
|
|
788 |
if (!chatMsg.startsWith("/me ") || (chatMsg.length() <= 4))
|
|
789 |
return chatMsg;
|
|
790 |
|
|
791 |
return MAGIC_BYTE_ACTION + "ACTION " + chatMsg.substring(4) + MAGIC_BYTE_ACTION;
|
|
792 |
}
|
|
793 |
|
|
794 |
private static String ircActionToHw(final String chatMsg) {
|
|
795 |
if (!chatMsg.startsWith(MAGIC_BYTE_ACTION + "ACTION ") || (chatMsg.length() <= 9))
|
|
796 |
return chatMsg;
|
|
797 |
|
|
798 |
return "/me " + chatMsg.substring(8, chatMsg.length() - 1);
|
|
799 |
}
|
|
800 |
|
|
801 |
// TODO: why is still still being called when joining bogus channel name?
|
|
802 |
public void join(String channelName)
|
|
803 |
{
|
|
804 |
if (ownPlayer == null) {
|
|
805 |
sendSelfNotice("Trying to join while ownPlayer == null. Aborting!");
|
|
806 |
quit("Something went horribly wrong.");
|
|
807 |
return;
|
|
808 |
}
|
|
809 |
|
|
810 |
|
|
811 |
final Channel channel = getChannel(channelName);
|
|
812 |
|
|
813 |
// TODO reserve special char for creating a new ROOM
|
|
814 |
// it will be named after the player name by default
|
|
815 |
// can be changed with /topic after
|
|
816 |
|
|
817 |
// not a valid channel
|
|
818 |
if (channel == null) {
|
|
819 |
sendSelfNotice("You cannot manually create channels here.");
|
|
820 |
sendGlobal(ERR_NOSUCHCHANNEL + ownIrcNick + " " + channel.name
|
|
821 |
+ " :No such channel");
|
|
822 |
return;
|
|
823 |
}
|
|
824 |
|
|
825 |
// TODO if inRoom "Can't join rooms while still in room"
|
|
826 |
|
|
827 |
// TODO set this based on room host/admin mode maybe
|
|
828 |
|
|
829 |
/* :testuser2131!~r@asdasdasdasd.at JOIN #asdkjasda
|
|
830 |
:weber.freenode.net MODE #asdkjasda +ns
|
|
831 |
:weber.freenode.net 353 testuser2131 @ #asdkjasda :@testuser2131
|
|
832 |
:weber.freenode.net 366 testuser2131 #asdkjasda :End of /NAMES list.
|
|
833 |
:weber.freenode.net NOTICE #asdkjasda :[freenode-info] why register and identify? your IRC nick is how people know you. http://freenode.net/faq.shtml#nicksetup
|
|
834 |
|
|
835 |
*/
|
|
836 |
send(":" + ownPlayer.getIrcId() + " JOIN "
|
|
837 |
+ channelName);
|
|
838 |
|
|
839 |
//send(":sheeppidgin!~r@localhost JOIN " + channelName);
|
|
840 |
|
|
841 |
ircJoined = true;
|
|
842 |
|
|
843 |
sendGlobal(":hw2irc MODE #lobby +nt");
|
|
844 |
|
|
845 |
sendTopic(channel);
|
|
846 |
|
|
847 |
sendNames(channel);
|
|
848 |
|
|
849 |
}
|
|
850 |
|
|
851 |
private void sendTopic(final Channel channel) {
|
|
852 |
if (channel.topic != null)
|
|
853 |
sendGlobal(RPL_TOPIC + ownIrcNick + " " + channel.name
|
|
854 |
+ " :" + channel.topic);
|
|
855 |
else
|
|
856 |
sendGlobal(RPL_NOTOPIC + ownIrcNick + " " + channel.name
|
|
857 |
+ " :No topic is set");
|
|
858 |
}
|
|
859 |
|
|
860 |
private void sendNames(final Channel channel) {
|
|
861 |
// There is no error reply for bad channel names.
|
|
862 |
|
|
863 |
if (channel != null) {
|
|
864 |
// send player list
|
|
865 |
for (final Player player : channel.players.values()) {
|
|
866 |
|
|
867 |
final String prefix;
|
|
868 |
|
|
869 |
if (player.isServerAdmin())
|
|
870 |
prefix = (player.isServerAdmin())?"@":"%";
|
|
871 |
else
|
|
872 |
prefix = (player.isRegistered())?"+":"";
|
|
873 |
|
|
874 |
sendGlobal(RPL_NAMREPLY + ownIrcNick + " = " + channel.name
|
|
875 |
+ " :" + prefix + player.ircNick);
|
|
876 |
}
|
|
877 |
}
|
|
878 |
|
|
879 |
sendGlobal(RPL_ENDOFNAMES + ownIrcNick + " " + channel.name
|
|
880 |
+ " :End of /NAMES list");
|
|
881 |
}
|
|
882 |
|
|
883 |
private void sendList(final String filter) {
|
|
884 |
// id column size
|
|
885 |
//int idl = 1 + String.valueOf(lastRoomId).length();
|
|
886 |
|
|
887 |
//if (idl < 3)
|
|
888 |
//idl = 3;
|
|
889 |
|
|
890 |
// send rooms list
|
|
891 |
sendGlobal(RPL_LISTSTART + ownIrcNick
|
|
892 |
//+ String.format(" %1$" + idl + "s #P #T Name", "ID"));
|
|
893 |
+ String.format(" %1$s #P #T Name", "ID"));
|
|
894 |
|
|
895 |
if (filter.isEmpty() || filter.equals(".")) {
|
|
896 |
// lobby
|
|
897 |
if (filter.isEmpty())
|
|
898 |
sendGlobal(RPL_LIST + ownIrcNick + " " + LOBBY_CHANNEL_NAME
|
|
899 |
+ " " + allPlayers.size() + " :" + lobbyChannel.topic);
|
|
900 |
|
|
901 |
// room list could be changed by server while we reply client
|
|
902 |
synchronized (roomsSorted) {
|
|
903 |
for (final Room room : roomsSorted) {
|
|
904 |
sendGlobal(RPL_LIST + ownIrcNick
|
|
905 |
//+ String.format(" %1$" + idl + "s %2$2d :%3$2d %4$s",
|
|
906 |
+ String.format(" %1$s %2$d :%3$d %4$s",
|
|
907 |
room.chan, room.nPlayers, room.nTeams, room.name));
|
|
908 |
}
|
|
909 |
}
|
|
910 |
}
|
|
911 |
// TODO filter
|
|
912 |
|
|
913 |
sendGlobal(RPL_LISTEND + ownIrcNick + " " + " :End of /LIST");
|
|
914 |
}
|
|
915 |
|
|
916 |
private List<Player> findPlayers(final String expr) {
|
|
917 |
List<Player> matches = new ArrayList<Player>(allPlayers.size());
|
|
918 |
|
|
919 |
try {
|
|
920 |
final int flags = Pattern.CASE_INSENSITIVE + Pattern.UNICODE_CASE;
|
|
921 |
final Pattern regx = Pattern.compile(expr, flags);
|
|
922 |
|
|
923 |
for (final Player p : allPlayers.values()) {
|
|
924 |
if ((regx.matcher(p.nick).find())
|
|
925 |
|| (regx.matcher(p.ircId).find())
|
|
926 |
//|| (regx.matcher(p.version).find())
|
|
927 |
//|| ((p.ip.length() > 2) && regx.matcher(p.ip).find())
|
|
928 |
|| (!p.getRoom().isEmpty() && regx.matcher(p.getRoom()).find())
|
|
929 |
) matches.add(p);
|
|
930 |
}
|
|
931 |
}
|
|
932 |
catch(PatternSyntaxException ex) {
|
|
933 |
sendSelfNotice("Pattern not understood: " + ex.getMessage());
|
|
934 |
}
|
|
935 |
|
|
936 |
return matches;
|
|
937 |
}
|
|
938 |
|
|
939 |
private String buildInfoString(final String ip, final String version, final String room) {
|
|
940 |
return (ip.equals("[]")?"":ip + " ") + version + (room.isEmpty()?"":" " + room);
|
|
941 |
}
|
|
942 |
|
|
943 |
private void sendWhoForPlayer(final Player player) {
|
|
944 |
sendWhoForPlayer(LOBBY_CHANNEL_NAME, player.ircNick, (player.inRoom?player.getRoom():""), player.getIrcHostname());
|
|
945 |
}
|
|
946 |
|
|
947 |
private void sendWhoForPlayer(final Player player, final String info) {
|
|
948 |
sendWhoForPlayer(LOBBY_CHANNEL_NAME, player.ircNick, info, player.getIrcHostname());
|
|
949 |
}
|
|
950 |
|
|
951 |
private void sendWhoForPlayer(final String nick, final String info) {
|
|
952 |
sendWhoForPlayer(LOBBY_CHANNEL_NAME, nick, info);
|
|
953 |
}
|
|
954 |
|
|
955 |
private void sendWhoForPlayer(final String channel, final String nick, final String info) {
|
|
956 |
final Player player = allPlayers.get(nick);
|
|
957 |
|
|
958 |
if (player == null)
|
|
959 |
sendWhoForPlayer("OFFLINE", hwToIrcNick(nick), info, "hw/offline");
|
|
960 |
else
|
|
961 |
sendWhoForPlayer(channel, player.ircNick, info, player.getIrcHostname());
|
|
962 |
}
|
|
963 |
|
|
964 |
private void sendWhoForPlayer(final String channel, final String ircNick, final String info, final String hostname) {
|
|
965 |
sendGlobal(RPL_WHOREPLY + channel + " " + channel
|
|
966 |
+ " ~" + ircNick + " " + hostname
|
|
967 |
+ " " + globalServerName + " " + ircNick
|
|
968 |
+ " H :0 " + info);
|
|
969 |
}
|
|
970 |
|
|
971 |
private void sendWhoEnd(final String ofWho) {
|
|
972 |
send(RPL_ENDOFWHO + ownIrcNick + " " + ofWho
|
|
973 |
+ " :End of /WHO list.");
|
|
974 |
}
|
|
975 |
|
|
976 |
private void sendMotd() {
|
|
977 |
sendGlobal(RPL_MOTDSTART + ownIrcNick + " :- Message of the Day -");
|
|
978 |
final String mline = RPL_MOTD + ownIrcNick + " :";
|
|
979 |
for(final String line : DEFAULT_MOTD) {
|
|
980 |
sendGlobal(mline + line);
|
|
981 |
}
|
|
982 |
sendGlobal(RPL_ENDOFMOTD + ownIrcNick + " :End of /MOTD command.");
|
|
983 |
}
|
|
984 |
|
|
985 |
private Channel getChannel(final String name) {
|
|
986 |
if (name.equals(LOBBY_CHANNEL_NAME)) {
|
|
987 |
return lobbyChannel;
|
|
988 |
}
|
|
989 |
|
|
990 |
return null;
|
|
991 |
}
|
|
992 |
|
|
993 |
private enum Command
|
|
994 |
{
|
|
995 |
PASS(1, 1)
|
|
996 |
{
|
|
997 |
@Override
|
|
998 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
999 |
throws Exception
|
|
1000 |
{
|
|
1001 |
con.passwordHash = args[0];
|
|
1002 |
}
|
|
1003 |
},
|
|
1004 |
NICK(1, 1)
|
|
1005 |
{
|
|
1006 |
@Override
|
|
1007 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1008 |
throws Exception
|
|
1009 |
{
|
|
1010 |
con.setNick(args[0]);
|
|
1011 |
}
|
|
1012 |
},
|
|
1013 |
USER(1, 4)
|
|
1014 |
{
|
|
1015 |
@Override
|
|
1016 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1017 |
throws Exception
|
|
1018 |
{
|
|
1019 |
if (con.username != null)
|
|
1020 |
{
|
|
1021 |
con.send("NOTICE AUTH :You can't change your user "
|
|
1022 |
+ "information after you've logged in right now.");
|
|
1023 |
return;
|
|
1024 |
}
|
|
1025 |
con.username = args[0];
|
|
1026 |
String forDescription = args.length > 3 ? args[3]
|
|
1027 |
: "(no description)";
|
|
1028 |
con.description = forDescription;
|
|
1029 |
/*
|
|
1030 |
* Now we'll send the user their initial information.
|
|
1031 |
*/
|
|
1032 |
con.sendGlobal(RPL_WELCOME + con.ownIrcNick + " :Welcome to "
|
|
1033 |
+ globalServerName + " - " + DESCRIPTION_SHORT);
|
|
1034 |
con.sendGlobal("004 " + con.ownIrcNick + " " + globalServerName
|
|
1035 |
+ " " + VERSION);
|
|
1036 |
|
|
1037 |
con.sendMotd();
|
|
1038 |
|
|
1039 |
}
|
|
1040 |
},
|
|
1041 |
MOTD(0, 0)
|
|
1042 |
{
|
|
1043 |
@Override
|
|
1044 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1045 |
throws Exception
|
|
1046 |
{
|
|
1047 |
con.sendMotd();
|
|
1048 |
}
|
|
1049 |
},
|
|
1050 |
PING(1, 1)
|
|
1051 |
{
|
|
1052 |
@Override
|
|
1053 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1054 |
throws Exception
|
|
1055 |
{
|
|
1056 |
con.ircPingQueue.add(args[0]);
|
|
1057 |
con.hwcon.sendPing();
|
|
1058 |
}
|
|
1059 |
},
|
|
1060 |
PONG(1, 2)
|
|
1061 |
{
|
|
1062 |
@Override
|
|
1063 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1064 |
throws Exception
|
|
1065 |
{
|
|
1066 |
con.hwcon.sendPong();
|
|
1067 |
}
|
|
1068 |
},
|
|
1069 |
NAMES(1, 1)
|
|
1070 |
{
|
|
1071 |
@Override
|
|
1072 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1073 |
throws Exception
|
|
1074 |
{
|
|
1075 |
final Channel channel = con.getChannel(args[0]);
|
|
1076 |
con.sendNames(channel);
|
|
1077 |
}
|
|
1078 |
},
|
|
1079 |
LIST(0, 2)
|
|
1080 |
{
|
|
1081 |
@Override
|
|
1082 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1083 |
throws Exception
|
|
1084 |
{
|
|
1085 |
// TODO filter by args[1] (comma sep list of chans), make # optional
|
|
1086 |
// ignore args[1] (server), TODO: maybe check and send RPL_NOSUCHSERVER if wrong
|
|
1087 |
con.sendList(args.length > 0?args[0]:"");
|
|
1088 |
}
|
|
1089 |
},
|
|
1090 |
JOIN(1, 2)
|
|
1091 |
{
|
|
1092 |
@Override
|
|
1093 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1094 |
throws Exception
|
|
1095 |
{
|
|
1096 |
if (args.length < 1) {
|
|
1097 |
con.sendSelfNotice("You didn't specify what you want to join!");
|
|
1098 |
return;
|
|
1099 |
}
|
|
1100 |
|
|
1101 |
if (con.ownPlayer == null) {
|
|
1102 |
con.sendSelfNotice("Lobby is not ready to be joined yet - hold on a second!");
|
|
1103 |
return;
|
|
1104 |
}
|
|
1105 |
|
|
1106 |
if (args[0].equals(LOBBY_CHANNEL_NAME)) {
|
|
1107 |
//con.sendSelfNotice("Lobby can't be joined manually!");
|
|
1108 |
con.join(LOBBY_CHANNEL_NAME);
|
|
1109 |
return;
|
|
1110 |
}
|
|
1111 |
con.sendSelfNotice("Joining rooms is not supported yet, sorry!");
|
|
1112 |
}
|
|
1113 |
},
|
|
1114 |
WHO(0, 2)
|
|
1115 |
{
|
|
1116 |
@Override
|
|
1117 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1118 |
throws Exception
|
|
1119 |
{
|
|
1120 |
if (args.length < 1)
|
|
1121 |
return;
|
|
1122 |
|
|
1123 |
final String target = args[0];
|
|
1124 |
|
|
1125 |
Map<String, Player> players = null;
|
|
1126 |
|
|
1127 |
if (target.equals(LOBBY_CHANNEL_NAME)) {
|
|
1128 |
players = con.allPlayers;
|
|
1129 |
}
|
|
1130 |
// on channel join WHO is called on channel
|
|
1131 |
else if (target.equals(ROOM_CHANNEL_NAME)) {
|
|
1132 |
players = con.roomPlayers;
|
|
1133 |
}
|
|
1134 |
|
|
1135 |
if (players != null) {
|
|
1136 |
for (final Player player : players.values()) {
|
|
1137 |
con.sendWhoForPlayer(player);
|
|
1138 |
}
|
|
1139 |
}
|
|
1140 |
// not a known channel. assume arg is player name
|
|
1141 |
// TODO support search expressions!
|
|
1142 |
else {
|
|
1143 |
final String nick = ircToHwNick(target);
|
|
1144 |
final Player player = con.allPlayers.get(nick);
|
|
1145 |
if (player != null)
|
|
1146 |
con.sendWhoForPlayer(player);
|
|
1147 |
else {
|
|
1148 |
con.sendSelfNotice("WHO: No player named " + nick + ", interpreting term as pattern.");
|
|
1149 |
List<Player> matches = con.findPlayers(target);
|
|
1150 |
if (matches.isEmpty())
|
|
1151 |
con.sendSelfNotice("No Match.");
|
|
1152 |
else {
|
|
1153 |
for (final Player match : matches) {
|
|
1154 |
con.sendWhoForPlayer(match);
|
|
1155 |
}
|
|
1156 |
}
|
|
1157 |
}
|
|
1158 |
}
|
|
1159 |
|
|
1160 |
con.sendWhoEnd(target);
|
|
1161 |
}
|
|
1162 |
},
|
|
1163 |
WHOIS(1, 2)
|
|
1164 |
{
|
|
1165 |
@Override
|
|
1166 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1167 |
throws Exception
|
|
1168 |
{
|
|
1169 |
// there's an optional param in the beginning that we don't care about
|
|
1170 |
final String targets = args[args.length-1];
|
|
1171 |
for (final String target : targets.split(",")) {
|
|
1172 |
if (target.isEmpty())
|
|
1173 |
continue;
|
|
1174 |
final String nick = ircToHwNick(target);
|
|
1175 |
// flag this nick as manually requested, so that response is shown
|
|
1176 |
if (con.ircJoined) {
|
|
1177 |
con.requestedInfos.add(nick);
|
|
1178 |
con.hwcon.requestInfo(nick);
|
|
1179 |
}
|
|
1180 |
|
|
1181 |
final Player player = con.allPlayers.get(nick);
|
|
1182 |
if (player != null) {
|
|
1183 |
con.send(RPL_WHOISUSER + con.ownIrcNick + " " + target + " ~"
|
|
1184 |
+ target + " " + player.getIrcHostname() + " * : "
|
|
1185 |
+ player.ircNick);
|
|
1186 |
// TODO send e.g. channels: @#lobby or @#123
|
|
1187 |
con.send(RPL_ENDOFWHOIS + con.ownIrcNick + " " + target
|
|
1188 |
+ " :End of /WHOIS list.");
|
|
1189 |
}
|
|
1190 |
}
|
|
1191 |
}
|
|
1192 |
},
|
|
1193 |
USERHOST(1, 5)
|
|
1194 |
{
|
|
1195 |
@Override
|
|
1196 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1197 |
throws Exception
|
|
1198 |
{
|
|
1199 |
/*
|
|
1200 |
// TODO set server host
|
|
1201 |
con.hostname = "hw/" + SERVER_HOST;
|
|
1202 |
|
|
1203 |
ArrayList<String> replies = new ArrayList<String>();
|
|
1204 |
for (String s : arguments)
|
|
1205 |
{
|
|
1206 |
Connection user = connectionMap.get(s);
|
|
1207 |
if (user != null)
|
|
1208 |
replies.add(user.nick + "=+" + con.ownIrc + "@"
|
|
1209 |
+ con.hostname);
|
|
1210 |
}
|
|
1211 |
con.sendGlobal(RPL_USERHOST + con.ownIrcNick + " :"
|
|
1212 |
+ delimited(replies.toArray(new String[0]), " "));
|
|
1213 |
*/
|
|
1214 |
}
|
|
1215 |
},
|
|
1216 |
MODE(0, 2)
|
|
1217 |
{
|
|
1218 |
@Override
|
|
1219 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1220 |
throws Exception
|
|
1221 |
{
|
|
1222 |
final boolean forChan = args[0].startsWith("#");
|
|
1223 |
|
|
1224 |
if (args.length == 1)
|
|
1225 |
{
|
|
1226 |
if (forChan) {
|
|
1227 |
//con.sendGlobal(ERR_NOCHANMODES + args[0]
|
|
1228 |
// + " :Channel doesn't support modes");
|
|
1229 |
con.sendGlobal(RPL_CHANNELMODEIS + con.ownIrcNick + " " + args[0]
|
|
1230 |
+ " +nt");
|
|
1231 |
}
|
|
1232 |
else
|
|
1233 |
{
|
|
1234 |
// TODO
|
|
1235 |
con.sendSelfNotice("User mode querying not supported yet.");
|
|
1236 |
}
|
|
1237 |
}
|
|
1238 |
else if (args.length == 2) {
|
|
1239 |
|
|
1240 |
if (forChan) {
|
|
1241 |
|
|
1242 |
final int l = args[1].length();
|
|
1243 |
|
|
1244 |
for (int i = 0; i < l; i++) {
|
|
1245 |
|
|
1246 |
final char c = args[1].charAt(i);
|
|
1247 |
|
|
1248 |
switch (c) {
|
|
1249 |
case '+':
|
|
1250 |
// skip
|
|
1251 |
break;
|
|
1252 |
case '-':
|
|
1253 |
// skip
|
|
1254 |
break;
|
|
1255 |
case 'b':
|
|
1256 |
con.sendGlobal(RPL_ENDOFBANLIST
|
|
1257 |
+ con.ownIrcNick + " " + args[0]
|
|
1258 |
+ " :End of channel ban list");
|
|
1259 |
break;
|
|
1260 |
case 'e':
|
|
1261 |
con.sendGlobal(RPL_ENDOFEXCEPTLIST
|
|
1262 |
+ con.ownIrcNick + " " + args[0]
|
|
1263 |
+ " :End of channel exception list");
|
|
1264 |
break;
|
|
1265 |
default:
|
|
1266 |
con.sendGlobal(ERR_UNKNOWNMODE + c
|
|
1267 |
+ " :Unknown MODE flag " + c);
|
|
1268 |
break;
|
|
1269 |
|
|
1270 |
}
|
|
1271 |
}
|
|
1272 |
}
|
|
1273 |
// user mode
|
|
1274 |
else {
|
|
1275 |
con.sendGlobal(ERR_UMODEUNKNOWNFLAG + args[0]
|
|
1276 |
+ " :Unknown MODE flag");
|
|
1277 |
}
|
|
1278 |
}
|
|
1279 |
else
|
|
1280 |
{
|
|
1281 |
con.sendSelfNotice("Specific modes not supported yet.");
|
|
1282 |
}
|
|
1283 |
}
|
|
1284 |
},
|
|
1285 |
PART(1, 2)
|
|
1286 |
{
|
|
1287 |
@Override
|
|
1288 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1289 |
throws Exception
|
|
1290 |
{
|
|
1291 |
String[] channels = args[0].split(",");
|
|
1292 |
boolean doQuit = false;
|
|
1293 |
|
|
1294 |
for (String channelName : channels)
|
|
1295 |
{
|
|
1296 |
if (channelName.equals(LOBBY_CHANNEL_NAME)) {
|
|
1297 |
doQuit = true;
|
|
1298 |
}
|
|
1299 |
// TODO: part from room
|
|
1300 |
/*
|
|
1301 |
synchronized (mutex)
|
|
1302 |
{
|
|
1303 |
Channel channel = channelMap.get(channelName);
|
|
1304 |
if (channelName == null)
|
|
1305 |
con
|
|
1306 |
.sendSelfNotice("You're not a member of the channel "
|
|
1307 |
+ channelName
|
|
1308 |
+ ", so you can't part it.");
|
|
1309 |
else
|
|
1310 |
{
|
|
1311 |
channel.send(":" + con.getRepresentation()
|
|
1312 |
+ " PART " + channelName);
|
|
1313 |
channel.channelMembers.remove(con);
|
|
1314 |
if (channel.channelMembers.size() == 0)
|
|
1315 |
channelMap.remove(channelName);
|
|
1316 |
}
|
|
1317 |
}
|
|
1318 |
*/
|
|
1319 |
}
|
|
1320 |
|
|
1321 |
final String reason;
|
|
1322 |
|
|
1323 |
if (args.length > 1)
|
|
1324 |
reason = args[1];
|
|
1325 |
else
|
|
1326 |
reason = DEFAULT_QUIT_REASON;
|
|
1327 |
|
|
1328 |
// quit after parting
|
|
1329 |
if (doQuit)
|
|
1330 |
con.quit(reason);
|
|
1331 |
}
|
|
1332 |
},
|
|
1333 |
QUIT(0, 1)
|
|
1334 |
{
|
|
1335 |
@Override
|
|
1336 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1337 |
throws Exception
|
|
1338 |
{
|
|
1339 |
final String reason;
|
|
1340 |
|
|
1341 |
if (args.length == 0)
|
|
1342 |
reason = DEFAULT_QUIT_REASON;
|
|
1343 |
else
|
|
1344 |
reason = args[0];
|
|
1345 |
|
|
1346 |
con.quit(reason);
|
|
1347 |
}
|
|
1348 |
},
|
|
1349 |
PRIVMSG(2, 2)
|
|
1350 |
{
|
|
1351 |
@Override
|
|
1352 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1353 |
throws Exception
|
|
1354 |
{
|
|
1355 |
String message = ircActionToHw(args[1]);
|
|
1356 |
if (message.charAt(0) == CHAT_COMMAND_CHAR) {
|
|
1357 |
if (message.length() < 1 )
|
|
1358 |
return;
|
|
1359 |
message = message.substring(1);
|
|
1360 |
// TODO maybe \rebind CUSTOMCMDCHAR command
|
|
1361 |
con.hwcon.sendCommand(con.ircToHwNick(message));
|
|
1362 |
}
|
|
1363 |
else
|
|
1364 |
con.hwcon.sendChat(con.ircToHwNick(message));
|
|
1365 |
}
|
|
1366 |
},
|
|
1367 |
TOPIC(1, 2)
|
|
1368 |
{
|
|
1369 |
@Override
|
|
1370 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1371 |
throws Exception
|
|
1372 |
{
|
|
1373 |
final String chan = args[0];
|
|
1374 |
|
|
1375 |
final Channel channel = con.getChannel(chan);
|
|
1376 |
|
|
1377 |
if (channel == null) {
|
|
1378 |
con.sendSelfNotice("No such channel for topic viewing: "
|
|
1379 |
+ chan);
|
|
1380 |
return;
|
|
1381 |
}
|
|
1382 |
|
|
1383 |
// The user wants to see the channel topic.
|
|
1384 |
if (args.length == 1)
|
|
1385 |
con.sendTopic(channel);
|
|
1386 |
// The user wants to set the channel topic.
|
|
1387 |
else
|
|
1388 |
channel.topic = args[1];
|
|
1389 |
}
|
|
1390 |
},
|
|
1391 |
KICK(3, 3)
|
|
1392 |
{
|
|
1393 |
@Override
|
|
1394 |
public void run(final Connection con, final String prefix, final String[] args)
|
|
1395 |
throws Exception
|
|
1396 |
{
|
|
1397 |
final String victim = args[1];
|
|
1398 |
con.logInfo("Issuing kick for " + victim);
|
|
1399 |
// "KICK #channel nick :kick reason (not relevant)"
|
|
1400 |
con.hwcon.kick(ircToHwNick(victim));
|
|
1401 |
}
|
|
1402 |
}
|
|
1403 |
;
|
|
1404 |
public final int minArgumentCount;
|
|
1405 |
public final int maxArgumentCount;
|
|
1406 |
|
|
1407 |
private Command(int min, int max)
|
|
1408 |
{
|
|
1409 |
minArgumentCount = min;
|
|
1410 |
maxArgumentCount = max;
|
|
1411 |
}
|
|
1412 |
|
|
1413 |
public abstract void run(Connection con, String prefix,
|
|
1414 |
String[] arguments) throws Exception;
|
|
1415 |
}
|
|
1416 |
|
|
1417 |
public static String delimited(String[] items, String delimiter)
|
|
1418 |
{
|
|
1419 |
StringBuffer response = new StringBuffer();
|
|
1420 |
boolean first = true;
|
|
1421 |
for (String s : items)
|
|
1422 |
{
|
|
1423 |
if (first)
|
|
1424 |
first = false;
|
|
1425 |
else
|
|
1426 |
response.append(delimiter);
|
|
1427 |
response.append(s);
|
|
1428 |
}
|
|
1429 |
return response.toString();
|
|
1430 |
}
|
|
1431 |
|
|
1432 |
protected void sendQuit(String quitMessage)
|
|
1433 |
{
|
|
1434 |
send(":" + getRepresentation() + " QUIT :" + quitMessage);
|
|
1435 |
}
|
|
1436 |
|
|
1437 |
@Override
|
|
1438 |
public void run()
|
|
1439 |
{
|
|
1440 |
try
|
|
1441 |
{
|
|
1442 |
doServer();
|
|
1443 |
}
|
|
1444 |
catch (Exception e) {
|
|
1445 |
e.printStackTrace();
|
|
1446 |
}
|
|
1447 |
finally
|
|
1448 |
{
|
|
1449 |
// TODO sense?
|
|
1450 |
if (ownIrcNick != null && connectionMap.get(ownIrcNick) == this) {
|
|
1451 |
quit("Client disconnected.");
|
|
1452 |
}
|
|
1453 |
|
|
1454 |
try {
|
|
1455 |
socket.close();
|
|
1456 |
}
|
|
1457 |
catch (Exception e) { }
|
|
1458 |
|
|
1459 |
quit("Connection terminated.");
|
|
1460 |
}
|
|
1461 |
}
|
|
1462 |
|
|
1463 |
protected void sendGlobal(String string)
|
|
1464 |
{
|
|
1465 |
send(":" + globalServerName + " " + string);
|
|
1466 |
}
|
|
1467 |
|
|
1468 |
private LinkedBlockingQueue<String> outQueue = new LinkedBlockingQueue<String>(
|
|
1469 |
1000);
|
|
1470 |
|
|
1471 |
private Thread outThread = new Thread()
|
|
1472 |
{
|
|
1473 |
public void run()
|
|
1474 |
{
|
|
1475 |
try
|
|
1476 |
{
|
|
1477 |
OutputStream out = socket.getOutputStream();
|
|
1478 |
while (!terminateConnection)
|
|
1479 |
{
|
|
1480 |
String s = outQueue.take();
|
|
1481 |
s = s.replace("\n", "").replace("\r", "");
|
|
1482 |
s = s + "\r\n";
|
|
1483 |
out.write(s.getBytes());
|
|
1484 |
out.flush();
|
|
1485 |
}
|
|
1486 |
}
|
|
1487 |
catch (Exception ex)
|
|
1488 |
{
|
|
1489 |
thisConnection.logError("Outqueue died");
|
|
1490 |
//ex.printStackTrace();
|
|
1491 |
}
|
|
1492 |
finally {
|
|
1493 |
outQueue.clear();
|
|
1494 |
outQueue = null;
|
|
1495 |
try
|
|
1496 |
{
|
|
1497 |
socket.close();
|
|
1498 |
}
|
|
1499 |
catch (Exception e2)
|
|
1500 |
{
|
|
1501 |
e2.printStackTrace();
|
|
1502 |
}
|
|
1503 |
}
|
|
1504 |
}
|
|
1505 |
};
|
|
1506 |
|
|
1507 |
private boolean terminateConnection = false;
|
|
1508 |
|
|
1509 |
private void doServer() throws Exception
|
|
1510 |
{
|
|
1511 |
outThread.start();
|
|
1512 |
InputStream socketIn = socket.getInputStream();
|
|
1513 |
BufferedReader clientReader = new BufferedReader(new InputStreamReader(
|
|
1514 |
socketIn));
|
|
1515 |
String line;
|
|
1516 |
while (!terminateConnection && ((line = clientReader.readLine()) != null))
|
|
1517 |
{
|
|
1518 |
processLine(line);
|
|
1519 |
}
|
|
1520 |
}
|
|
1521 |
|
|
1522 |
public void sanitizeInputs(final String[] inputs) {
|
|
1523 |
|
|
1524 |
// no for-each loop, because we need write access to the elements
|
|
1525 |
|
|
1526 |
final int l = inputs.length;
|
|
1527 |
|
|
1528 |
for (int i = 0; i < l; i++) {
|
|
1529 |
inputs[i] = inputs[i].replaceAll(MAGIC_BYTES, " ");
|
|
1530 |
}
|
|
1531 |
}
|
|
1532 |
|
|
1533 |
private void processLine(final String line) throws Exception
|
|
1534 |
{
|
|
1535 |
String l = line;
|
|
1536 |
|
|
1537 |
// log things
|
|
1538 |
if (l.startsWith("PASS") || l.startsWith("pass"))
|
|
1539 |
this.logInfo("IRC-Client provided PASS");
|
|
1540 |
else
|
|
1541 |
this.logDebug("IRC-Client: " + l);
|
|
1542 |
|
|
1543 |
String prefix = "";
|
|
1544 |
if (l.startsWith(":"))
|
|
1545 |
{
|
|
1546 |
String[] tokens = l.split(" ", 2);
|
|
1547 |
prefix = tokens[0];
|
|
1548 |
l = (tokens.length > 1 ? tokens[1] : "");
|
|
1549 |
}
|
|
1550 |
String[] tokens1 = l.split(" ", 2);
|
|
1551 |
String command = tokens1[0];
|
|
1552 |
l = tokens1.length > 1 ? tokens1[1] : "";
|
|
1553 |
String[] tokens2 = l.split("(^| )\\:", 2);
|
|
1554 |
String trailing = null;
|
|
1555 |
l = tokens2[0];
|
|
1556 |
if (tokens2.length > 1)
|
|
1557 |
trailing = tokens2[1];
|
|
1558 |
ArrayList<String> argumentList = new ArrayList<String>();
|
|
1559 |
if (!l.equals(""))
|
|
1560 |
argumentList.addAll(Arrays.asList(l.split(" ")));
|
|
1561 |
if (trailing != null)
|
|
1562 |
argumentList.add(trailing);
|
|
1563 |
final String[] args = argumentList.toArray(new String[0]);
|
|
1564 |
|
|
1565 |
// process command
|
|
1566 |
|
|
1567 |
// numeric commands
|
|
1568 |
if (command.matches("[0-9][0-9][0-9]"))
|
|
1569 |
command = "N" + command;
|
|
1570 |
|
|
1571 |
final Command commandObject;
|
|
1572 |
|
|
1573 |
try {
|
|
1574 |
commandObject = Command.valueOf(command.toUpperCase());
|
|
1575 |
}
|
|
1576 |
catch (Exception ex) {
|
|
1577 |
// forward raw unknown command to hw server
|
|
1578 |
hwcon.sendCommand(ircToHwNick(line));
|
|
1579 |
return;
|
|
1580 |
}
|
|
1581 |
|
|
1582 |
if (args.length < commandObject.minArgumentCount
|
|
1583 |
|| args.length > commandObject.maxArgumentCount)
|
|
1584 |
{
|
|
1585 |
sendSelfNotice("Invalid number of arguments for this"
|
|
1586 |
+ " command, expected not more than "
|
|
1587 |
+ commandObject.maxArgumentCount + " and not less than "
|
|
1588 |
+ commandObject.minArgumentCount + " but got " + args.length
|
|
1589 |
+ " arguments");
|
|
1590 |
return;
|
|
1591 |
}
|
|
1592 |
commandObject.run(this, prefix, args);
|
|
1593 |
}
|
|
1594 |
|
|
1595 |
/**
|
|
1596 |
* Sends a notice from the server to the user represented by this
|
|
1597 |
* connection.
|
|
1598 |
*
|
|
1599 |
* @param string
|
|
1600 |
* The text to send as a notice
|
|
1601 |
*/
|
|
1602 |
|
|
1603 |
private void sendSelfNotice(final String string)
|
|
1604 |
{
|
|
1605 |
send(":" + globalServerName + " NOTICE " + ownIrcNick + " :" + string);
|
|
1606 |
}
|
|
1607 |
|
|
1608 |
private void sendChannelNotice(final String string) {
|
|
1609 |
sendChannelNotice(string, globalServerName);
|
|
1610 |
}
|
|
1611 |
|
|
1612 |
private void sendChannelNotice(final String string, final String from) {
|
|
1613 |
// TODO send to room if user is in room
|
|
1614 |
send(":" + from + " NOTICE " + LOBBY_CHANNEL_NAME + " :" + string);
|
|
1615 |
}
|
|
1616 |
|
|
1617 |
private void sendServerNotice(final String string)
|
|
1618 |
{
|
|
1619 |
if (ircJoined)
|
|
1620 |
sendChannelNotice(string, "[INFO]");
|
|
1621 |
|
|
1622 |
sendSelfNotice(string);
|
|
1623 |
}
|
|
1624 |
|
|
1625 |
private String[] padSplit(final String line, final String regex, int max)
|
|
1626 |
{
|
|
1627 |
String[] split = line.split(regex);
|
|
1628 |
String[] output = new String[max];
|
|
1629 |
for (int i = 0; i < output.length; i++)
|
|
1630 |
{
|
|
1631 |
output[i] = "";
|
|
1632 |
}
|
|
1633 |
for (int i = 0; i < split.length; i++)
|
|
1634 |
{
|
|
1635 |
output[i] = split[i];
|
|
1636 |
}
|
|
1637 |
return output;
|
|
1638 |
}
|
|
1639 |
|
|
1640 |
public void sendIfJoined(final String s) {
|
|
1641 |
if (joined)
|
|
1642 |
send(s);
|
|
1643 |
}
|
|
1644 |
|
|
1645 |
public void send(final String s)
|
|
1646 |
{
|
|
1647 |
final Queue<String> testQueue = outQueue;
|
|
1648 |
if (testQueue != null)
|
|
1649 |
{
|
|
1650 |
this.logDebug("IRC-Server: " + s);
|
|
1651 |
testQueue.add(s);
|
|
1652 |
}
|
|
1653 |
}
|
|
1654 |
|
|
1655 |
final static String RPL_WELCOME = "001 ";
|
|
1656 |
final static String RPL_YOURHOST = "002 ";
|
|
1657 |
final static String RPL_CREATED = "003 ";
|
|
1658 |
final static String RPL_MYINFO = "004 ";
|
|
1659 |
final static String RPL_BOUNCE = "005 ";
|
|
1660 |
final static String RPL_TRACELINK = "200 ";
|
|
1661 |
final static String RPL_TRACECONNECTING = "201 ";
|
|
1662 |
final static String RPL_TRACEHANDSHAKE = "202 ";
|
|
1663 |
final static String RPL_TRACEUNKNOWN = "203 ";
|
|
1664 |
final static String RPL_TRACEOPERATOR = "204 ";
|
|
1665 |
final static String RPL_TRACEUSER = "205 ";
|
|
1666 |
final static String RPL_TRACESERVER = "206 ";
|
|
1667 |
final static String RPL_TRACESERVICE = "207 ";
|
|
1668 |
final static String RPL_TRACENEWTYPE = "208 ";
|
|
1669 |
final static String RPL_TRACECLASS = "209 ";
|
|
1670 |
final static String RPL_TRACERECONNECT = "210 ";
|
|
1671 |
final static String RPL_STATSLINKINFO = "211 ";
|
|
1672 |
final static String RPL_STATSCOMMANDS = "212 ";
|
|
1673 |
final static String RPL_STATSCLINE = "213 ";
|
|
1674 |
final static String RPL_STATSNLINE = "214 ";
|
|
1675 |
final static String RPL_STATSILINE = "215 ";
|
|
1676 |
final static String RPL_STATSKLINE = "216 ";
|
|
1677 |
final static String RPL_STATSQLINE = "217 ";
|
|
1678 |
final static String RPL_STATSYLINE = "218 ";
|
|
1679 |
final static String RPL_ENDOFSTATS = "219 ";
|
|
1680 |
final static String RPL_UMODEIS = "221 ";
|
|
1681 |
final static String RPL_SERVICEINFO = "231 ";
|
|
1682 |
final static String RPL_ENDOFSERVICES = "232 ";
|
|
1683 |
final static String RPL_SERVICE = "233 ";
|
|
1684 |
final static String RPL_SERVLIST = "234 ";
|
|
1685 |
final static String RPL_SERVLISTEND = "235 ";
|
|
1686 |
final static String RPL_STATSVLINE = "240 ";
|
|
1687 |
final static String RPL_STATSLLINE = "241 ";
|
|
1688 |
final static String RPL_STATSUPTIME = "242 ";
|
|
1689 |
final static String RPL_STATSOLINE = "243 ";
|
|
1690 |
final static String RPL_STATSHLINE = "244 ";
|
|
1691 |
final static String RPL_STATSPING = "246 ";
|
|
1692 |
final static String RPL_STATSBLINE = "247 ";
|
|
1693 |
final static String RPL_STATSDLINE = "250 ";
|
|
1694 |
final static String RPL_LUSERCLIENT = "251 ";
|
|
1695 |
final static String RPL_LUSEROP = "252 ";
|
|
1696 |
final static String RPL_LUSERUNKNOWN = "253 ";
|
|
1697 |
final static String RPL_LUSERCHANNELS = "254 ";
|
|
1698 |
final static String RPL_LUSERME = "255 ";
|
|
1699 |
final static String RPL_ADMINME = "256 ";
|
|
1700 |
final static String RPL_ADMINEMAIL = "259 ";
|
|
1701 |
final static String RPL_TRACELOG = "261 ";
|
|
1702 |
final static String RPL_TRACEEND = "262 ";
|
|
1703 |
final static String RPL_TRYAGAIN = "263 ";
|
|
1704 |
final static String RPL_NONE = "300 ";
|
|
1705 |
final static String RPL_AWAY = "301 ";
|
|
1706 |
final static String RPL_USERHOST = "302 ";
|
|
1707 |
final static String RPL_ISON = "303 ";
|
|
1708 |
final static String RPL_UNAWAY = "305 ";
|
|
1709 |
final static String RPL_NOWAWAY = "306 ";
|
|
1710 |
final static String RPL_WHOISUSER = "311 ";
|
|
1711 |
final static String RPL_WHOISSERVER = "312 ";
|
|
1712 |
final static String RPL_WHOISOPERATOR = "313 ";
|
|
1713 |
final static String RPL_WHOWASUSER = "314 ";
|
|
1714 |
final static String RPL_ENDOFWHO = "315 ";
|
|
1715 |
final static String RPL_WHOISCHANOP = "316 ";
|
|
1716 |
final static String RPL_WHOISIDLE = "317 ";
|
|
1717 |
final static String RPL_ENDOFWHOIS = "318 ";
|
|
1718 |
final static String RPL_WHOISCHANNELS = "319 ";
|
|
1719 |
final static String RPL_LISTSTART = "321 ";
|
|
1720 |
final static String RPL_LIST = "322 ";
|
|
1721 |
final static String RPL_LISTEND = "323 ";
|
|
1722 |
final static String RPL_CHANNELMODEIS = "324 ";
|
|
1723 |
final static String RPL_UNIQOPIS = "325 ";
|
|
1724 |
final static String RPL_NOTOPIC = "331 ";
|
|
1725 |
final static String RPL_TOPIC = "332 ";
|
|
1726 |
final static String RPL_INVITING = "341 ";
|
|
1727 |
final static String RPL_SUMMONING = "342 ";
|
|
1728 |
final static String RPL_INVITELIST = "346 ";
|
|
1729 |
final static String RPL_ENDOFINVITELIST = "347 ";
|
|
1730 |
final static String RPL_EXCEPTLIST = "348 ";
|
|
1731 |
final static String RPL_ENDOFEXCEPTLIST = "349 ";
|
|
1732 |
final static String RPL_VERSION = "351 ";
|
|
1733 |
final static String RPL_WHOREPLY = "352 ";
|
|
1734 |
final static String RPL_NAMREPLY = "353 ";
|
|
1735 |
final static String RPL_KILLDONE = "361 ";
|
|
1736 |
final static String RPL_CLOSING = "362 ";
|
|
1737 |
final static String RPL_CLOSEEND = "363 ";
|
|
1738 |
final static String RPL_LINKS = "364 ";
|
|
1739 |
final static String RPL_ENDOFLINKS = "365 ";
|
|
1740 |
final static String RPL_ENDOFNAMES = "366 ";
|
|
1741 |
final static String RPL_BANLIST = "367 ";
|
|
1742 |
final static String RPL_ENDOFBANLIST = "368 ";
|
|
1743 |
final static String RPL_ENDOFWHOWAS = "369 ";
|
|
1744 |
final static String RPL_INFO = "371 ";
|
|
1745 |
final static String RPL_MOTD = "372 ";
|
|
1746 |
final static String RPL_INFOSTART = "373 ";
|
|
1747 |
final static String RPL_ENDOFINFO = "374 ";
|
|
1748 |
final static String RPL_MOTDSTART = "375 ";
|
|
1749 |
final static String RPL_ENDOFMOTD = "376 ";
|
|
1750 |
final static String RPL_YOUREOPER = "381 ";
|
|
1751 |
final static String RPL_REHASHING = "382 ";
|
|
1752 |
final static String RPL_YOURESERVICE = "383 ";
|
|
1753 |
final static String RPL_MYPORTIS = "384 ";
|
|
1754 |
final static String RPL_TIME = "391 ";
|
|
1755 |
final static String RPL_USERSSTART = "392 ";
|
|
1756 |
final static String RPL_USERS = "393 ";
|
|
1757 |
final static String RPL_ENDOFUSERS = "394 ";
|
|
1758 |
final static String RPL_NOUSERS = "395 ";
|
|
1759 |
final static String ERR_NOSUCHNICK = "401 ";
|
|
1760 |
final static String ERR_NOSUCHSERVER = "402 ";
|
|
1761 |
final static String ERR_NOSUCHCHANNEL = "403 ";
|
|
1762 |
final static String ERR_CANNOTSENDTOCHAN = "404 ";
|
|
1763 |
final static String ERR_TOOMANYCHANNELS = "405 ";
|
|
1764 |
final static String ERR_WASNOSUCHNICK = "406 ";
|
|
1765 |
final static String ERR_TOOMANYTARGETS = "407 ";
|
|
1766 |
final static String ERR_NOSUCHSERVICE = "408 ";
|
|
1767 |
final static String ERR_NOORIGIN = "409 ";
|
|
1768 |
final static String ERR_NORECIPIENT = "411 ";
|
|
1769 |
final static String ERR_NOTEXTTOSEND = "412 ";
|
|
1770 |
final static String ERR_NOTOPLEVEL = "413 ";
|
|
1771 |
final static String ERR_WILDTOPLEVEL = "414 ";
|
|
1772 |
final static String ERR_BADMASK = "415 ";
|
|
1773 |
final static String ERR_UNKNOWNCOMMAND = "421 ";
|
|
1774 |
final static String ERR_NOMOTD = "422 ";
|
|
1775 |
final static String ERR_NOADMININFO = "423 ";
|
|
1776 |
final static String ERR_FILEERROR = "424 ";
|
|
1777 |
final static String ERR_NONICKNAMEGIVEN = "431 ";
|
|
1778 |
final static String ERR_ERRONEUSNICKNAME = "432 ";
|
|
1779 |
final static String ERR_NICKNAMEINUSE = "433 ";
|
|
1780 |
final static String ERR_NICKCOLLISION = "436 ";
|
|
1781 |
final static String ERR_UNAVAILRESOURCE = "437 ";
|
|
1782 |
final static String ERR_USERNOTINCHANNEL = "441 ";
|
|
1783 |
final static String ERR_NOTONCHANNEL = "442 ";
|
|
1784 |
final static String ERR_USERONCHANNEL = "443 ";
|
|
1785 |
final static String ERR_NOLOGIN = "444 ";
|
|
1786 |
final static String ERR_SUMMONDISABLED = "445 ";
|
|
1787 |
final static String ERR_USERSDISABLED = "446 ";
|
|
1788 |
final static String ERR_NOTREGISTERED = "451 ";
|
|
1789 |
final static String ERR_NEEDMOREPARAMS = "461 ";
|
|
1790 |
final static String ERR_ALREADYREGISTRED = "462 ";
|
|
1791 |
final static String ERR_NOPERMFORHOST = "463 ";
|
|
1792 |
final static String ERR_PASSWDMISMATCH = "464 ";
|
|
1793 |
final static String ERR_YOUREBANNEDCREEP = "465 ";
|
|
1794 |
final static String ERR_YOUWILLBEBANNED = "466 ";
|
|
1795 |
final static String ERR_KEYSET = "467 ";
|
|
1796 |
final static String ERR_CHANNELISFULL = "471 ";
|
|
1797 |
final static String ERR_UNKNOWNMODE = "472 ";
|
|
1798 |
final static String ERR_INVITEONLYCHAN = "473 ";
|
|
1799 |
final static String ERR_BANNEDFROMCHAN = "474 ";
|
|
1800 |
final static String ERR_BADCHANNELKEY = "475 ";
|
|
1801 |
final static String ERR_BADCHANMASK = "476 ";
|
|
1802 |
final static String ERR_NOCHANMODES = "477 ";
|
|
1803 |
final static String ERR_BANLISTFULL = "478 ";
|
|
1804 |
final static String ERR_NOPRIVILEGES = "481 ";
|
|
1805 |
final static String ERR_CHANOPRIVSNEEDED = "482 ";
|
|
1806 |
final static String ERR_CANTKILLSERVER = "483 ";
|
|
1807 |
final static String ERR_RESTRICTED = "484 ";
|
|
1808 |
final static String ERR_UNIQOPPRIVSNEEDED = "485 ";
|
|
1809 |
final static String ERR_NOOPERHOST = "491 ";
|
|
1810 |
final static String ERR_NOSERVICEHOST = "492 ";
|
|
1811 |
final static String ERR_UMODEUNKNOWNFLAG = "501 ";
|
|
1812 |
final static String ERR_USERSDONTMATCH = "502 ";
|
|
1813 |
|
|
1814 |
}
|