00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package com.realtime.crossfire.jxclient.server.crossfire;
00023
00024 import com.realtime.crossfire.jxclient.guistate.ClientSocketState;
00025 import com.realtime.crossfire.jxclient.map.Location;
00026 import com.realtime.crossfire.jxclient.server.crossfire.messages.Map2;
00027 import com.realtime.crossfire.jxclient.server.crossfire.messages.UpdItem;
00028 import com.realtime.crossfire.jxclient.server.server.DefaultServerConnection;
00029 import com.realtime.crossfire.jxclient.server.socket.ClientSocketListener;
00030 import com.realtime.crossfire.jxclient.server.socket.UnknownCommandException;
00031 import com.realtime.crossfire.jxclient.spells.SpellsManager;
00032 import com.realtime.crossfire.jxclient.stats.Stats;
00033 import com.realtime.crossfire.jxclient.util.DebugWriter;
00034 import com.realtime.crossfire.jxclient.util.HexCodec;
00035 import java.io.BufferedReader;
00036 import java.io.ByteArrayInputStream;
00037 import java.io.IOException;
00038 import java.io.InputStreamReader;
00039 import java.io.UnsupportedEncodingException;
00040 import java.nio.BufferUnderflowException;
00041 import java.nio.ByteBuffer;
00042 import java.nio.ByteOrder;
00043 import java.util.ArrayList;
00044 import java.util.Arrays;
00045 import java.util.List;
00046 import java.util.regex.Pattern;
00047 import org.jetbrains.annotations.NotNull;
00048 import org.jetbrains.annotations.Nullable;
00049
00054 public class DefaultCrossfireServerConnection extends AbstractCrossfireServerConnection {
00055
00059 private static final int DEFAULT_MAP_WIDTH = 11;
00060
00064 private static final int DEFAULT_MAP_HEIGHT = 11;
00065
00070 private static final int DEFAULT_NUM_LOOK_OBJECTS = 50;
00071
00075 @NotNull
00076 private static final Pattern PATTERN_DOT = Pattern.compile(":");
00077
00081 private static final int ACL_NAME = 1;
00082
00086 private static final int ACL_CLASS = 2;
00087
00091 private static final int ACL_RACE = 3;
00092
00096 private static final int ACL_LEVEL = 4;
00097
00101 private static final int ACL_FACE = 5;
00102
00106 private static final int ACL_PARTY = 6;
00107
00111 private static final int ACL_MAP = 7;
00112
00116 private static final int ACL_FACE_NUM = 8;
00117
00121 @NotNull
00122 private final Model model;
00123
00127 @NotNull
00128 private final DefaultServerConnection defaultServerConnection;
00129
00133 private int preferredMapWidth = 17;
00134
00138 private int preferredMapHeight = 13;
00139
00144 private int pendingMapWidth = 0;
00145
00150 private int pendingMapHeight = 0;
00151
00155 private int currentMapWidth = DEFAULT_MAP_WIDTH;
00156
00160 private int currentMapHeight = DEFAULT_MAP_HEIGHT;
00161
00165 private int preferredNumLookObjects = DEFAULT_NUM_LOOK_OBJECTS;
00166
00171 private int pendingNumLookObjects = 0;
00172
00176 private int currentNumLookObjects = 0;
00177
00182 @NotNull
00183 private final byte[] writeBuffer = new byte[65536];
00184
00188 @NotNull
00189 private final ByteBuffer byteBuffer = ByteBuffer.wrap(writeBuffer);
00190
00194 private int packet = 1;
00195
00199 @NotNull
00200 private static final byte[] ACCOUNT_LOGIN_PREFIX = {
00201 'a',
00202 'c',
00203 'c',
00204 'o',
00205 'u',
00206 'n',
00207 't',
00208 'l',
00209 'o',
00210 'g',
00211 'i',
00212 'n',
00213 ' ',
00214 };
00215
00219 @NotNull
00220 private static final byte[] ACCOUNT_PLAY_PREFIX = {
00221 'a',
00222 'c',
00223 'c',
00224 'o',
00225 'u',
00226 'n',
00227 't',
00228 'p',
00229 'l',
00230 'a',
00231 'y',
00232 ' ',
00233 };
00234
00238 @NotNull
00239 private static final byte[] ACCOUNT_ADD_PLAYER_PREFIX = {
00240 'a',
00241 'c',
00242 'c',
00243 'o',
00244 'u',
00245 'n',
00246 't',
00247 'a',
00248 'd',
00249 'd',
00250 'p',
00251 'l',
00252 'a',
00253 'y',
00254 'e',
00255 'r',
00256 ' ',
00257 };
00258
00262 @NotNull
00263 private static final byte[] ACCOUNT_NEW_PREFIX = {
00264 'a',
00265 'c',
00266 'c',
00267 'o',
00268 'u',
00269 'n',
00270 't',
00271 'n',
00272 'e',
00273 'w',
00274 ' ',
00275 };
00276
00280 @NotNull
00281 private static final byte[] ACCOUNT_PASSWORD_PREFIX = {
00282 'a',
00283 'c',
00284 'c',
00285 'o',
00286 'u',
00287 'n',
00288 't',
00289 'p',
00290 'w',
00291 ' ',
00292 };
00293
00297 @NotNull
00298 private static final byte[] CREATE_PLAYER_PREFIX = {
00299 'c',
00300 'r',
00301 'e',
00302 'a',
00303 't',
00304 'e',
00305 'p',
00306 'l',
00307 'a',
00308 'y',
00309 'e',
00310 'r',
00311 ' ',
00312 };
00313
00317 @NotNull
00318 private static final byte[] ADDME_PREFIX = {
00319 'a',
00320 'd',
00321 'd',
00322 'm',
00323 'e',
00324 ' ',
00325 };
00326
00330 @NotNull
00331 private static final byte[] APPLY_PREFIX = {
00332 'a',
00333 'p',
00334 'p',
00335 'l',
00336 'y',
00337 ' ',
00338 };
00339
00343 @NotNull
00344 private static final byte[] ASKFACE_PREFIX = {
00345 'a',
00346 's',
00347 'k',
00348 'f',
00349 'a',
00350 'c',
00351 'e',
00352 ' ',
00353 };
00354
00358 @NotNull
00359 private static final byte[] EXAMINE_PREFIX = {
00360 'e',
00361 'x',
00362 'a',
00363 'm',
00364 'i',
00365 'n',
00366 'e',
00367 ' ',
00368 };
00369
00373 @NotNull
00374 private static final byte[] LOCK_PREFIX = {
00375 'l',
00376 'o',
00377 'c',
00378 'k',
00379 ' ',
00380 };
00381
00385 @NotNull
00386 private static final byte[] LOOKAT_PREFIX = {
00387 'l',
00388 'o',
00389 'o',
00390 'k',
00391 'a',
00392 't',
00393 ' ',
00394 };
00395
00399 @NotNull
00400 private static final byte[] MARK_PREFIX = {
00401 'm',
00402 'a',
00403 'r',
00404 'k',
00405 ' ',
00406 };
00407
00411 @NotNull
00412 private static final byte[] MOVE_PREFIX = {
00413 'm',
00414 'o',
00415 'v',
00416 'e',
00417 ' ',
00418 };
00419
00423 @NotNull
00424 private static final byte[] NCOM_PREFIX = {
00425 'n',
00426 'c',
00427 'o',
00428 'm',
00429 ' ',
00430 };
00431
00435 @NotNull
00436 private static final byte[] REPLY_PREFIX = {
00437 'r',
00438 'e',
00439 'p',
00440 'l',
00441 'y',
00442 ' ',
00443 };
00444
00448 @NotNull
00449 private static final byte[] REQUESTINFO_PREFIX = {
00450 'r',
00451 'e',
00452 'q',
00453 'u',
00454 'e',
00455 's',
00456 't',
00457 'i',
00458 'n',
00459 'f',
00460 'o',
00461 ' ',
00462 };
00463
00467 @NotNull
00468 private static final byte[] SETUP_PREFIX = {
00469 's',
00470 'e',
00471 't',
00472 'u',
00473 'p',
00474 };
00475
00479 @NotNull
00480 private static final byte[] TOGGLEEXTENDEDTEXT_PREFIX = {
00481 't',
00482 'o',
00483 'g',
00484 'g',
00485 'l',
00486 'e',
00487 'e',
00488 'x',
00489 't',
00490 'e',
00491 'n',
00492 'd',
00493 'e',
00494 'd',
00495 't',
00496 'e',
00497 'x',
00498 't',
00499 };
00500
00504 @NotNull
00505 private static final byte[] VERSION_PREFIX = {
00506 'v',
00507 'e',
00508 'r',
00509 's',
00510 'i',
00511 'o',
00512 'n',
00513 ' ',
00514 };
00515
00519 @NotNull
00520 private final String version;
00521
00526 @Nullable
00527 private final DebugWriter debugProtocol;
00528
00532 @NotNull
00533 private ClientSocketState clientSocketState = ClientSocketState.CONNECTING;
00534
00538 @Nullable
00539 private String accountName = null;
00540
00544 @NotNull
00545 private final ClientSocketListener clientSocketListener = new ClientSocketListener() {
00546
00547 @Override
00548 public void connecting() {
00549
00550 }
00551
00552 @Override
00553 public void connected() {
00554 DefaultCrossfireServerConnection.this.connected();
00555 }
00556
00557 @Override
00558 public void packetReceived(@NotNull final ByteBuffer packet) throws UnknownCommandException {
00559 processPacket(packet);
00560 }
00561
00562 @Override
00563 public void packetSent(@NotNull final byte[] buf, final int len) {
00564
00565 }
00566
00567 @Override
00568 public void disconnecting(@NotNull final String reason, final boolean isError) {
00569
00570 }
00571
00572 @Override
00573 public void disconnected(@NotNull final String reason) {
00574
00575 }
00576
00577 };
00578
00582 private int loginMethod = 0;
00583
00593 public DefaultCrossfireServerConnection(@NotNull final Model model, @Nullable final DebugWriter debugProtocol, @NotNull final String version) throws IOException {
00594 super(model);
00595 this.model = model;
00596 defaultServerConnection = new DefaultServerConnection(model, debugProtocol);
00597 this.version = version;
00598 byteBuffer.order(ByteOrder.BIG_ENDIAN);
00599 this.debugProtocol = debugProtocol;
00600 addClientSocketListener(clientSocketListener);
00601 }
00602
00606 @Override
00607 public void start() {
00608 defaultServerConnection.start();
00609 }
00610
00614 @Override
00615 public void stop() throws InterruptedException {
00616 defaultServerConnection.stop();
00617 }
00618
00622 private void connected() {
00623 pendingMapWidth = 0;
00624 pendingMapHeight = 0;
00625 pendingNumLookObjects = 0;
00626 if (debugProtocol != null) {
00627 debugProtocol.debugProtocolWrite("connected: defaulting to pending_num_look_objects="+pendingNumLookObjects);
00628 }
00629 setCurrentMapSize(DEFAULT_MAP_WIDTH, DEFAULT_MAP_HEIGHT);
00630 currentNumLookObjects = DEFAULT_NUM_LOOK_OBJECTS;
00631 if (debugProtocol != null) {
00632 debugProtocol.debugProtocolWrite("connected: defaulting to num_look_objects="+currentNumLookObjects);
00633 }
00634
00635 fireNewMap();
00636
00637 setClientSocketState(ClientSocketState.CONNECTING, ClientSocketState.VERSION);
00638 sendVersion(1023, 1027, version);
00639 }
00640
00648 private void processPacket(@NotNull final ByteBuffer packet) throws UnknownCommandException {
00649 try {
00650 packet.mark();
00651 switch (packet.get()) {
00652 case 'a':
00653 switch (packet.get()) {
00654 case 'c':
00655
00656 if (packet.get() != 'c') {
00657 break;
00658 }
00659 if (packet.get() != 'o') {
00660 break;
00661 }
00662 if (packet.get() != 'u') {
00663 break;
00664 }
00665 if (packet.get() != 'n') {
00666 break;
00667 }
00668 if (packet.get() != 't') {
00669 break;
00670 }
00671 if (packet.get() != 'p') {
00672 break;
00673 }
00674 if (packet.get() != 'l') {
00675 break;
00676 }
00677 if (packet.get() != 'a') {
00678 break;
00679 }
00680 if (packet.get() != 'y') {
00681 break;
00682 }
00683 if (packet.get() != 'e') {
00684 break;
00685 }
00686 if (packet.get() != 'r') {
00687 break;
00688 }
00689 if (packet.get() != 's') {
00690 break;
00691 }
00692 if (packet.get() != ' ') {
00693 break;
00694 }
00695 if (debugProtocol != null) {
00696 debugProtocol.debugProtocolWrite("recv accountplayers");
00697 }
00698 processAccountPlayers(packet);
00699 return;
00700
00701 case 'd':
00702 if (packet.get() != 'd') {
00703 break;
00704 }
00705 switch (packet.get()) {
00706 case 'm':
00707 if (packet.get() != 'e') {
00708 break;
00709 }
00710 if (packet.get() != '_') {
00711 break;
00712 }
00713 switch (packet.get()) {
00714 case 'f':
00715 if (packet.get() != 'a') {
00716 break;
00717 }
00718 if (packet.get() != 'i') {
00719 break;
00720 }
00721 if (packet.get() != 'l') {
00722 break;
00723 }
00724 if (packet.get() != 'e') {
00725 break;
00726 }
00727 if (packet.get() != 'd') {
00728 break;
00729 }
00730 if (packet.hasRemaining()) {
00731 break;
00732 }
00733 if (debugProtocol != null) {
00734 debugProtocol.debugProtocolWrite("recv addme_failed");
00735 }
00736 processAddmeFailed(packet);
00737 return;
00738
00739 case 's':
00740 if (packet.get() != 'u') {
00741 break;
00742 }
00743 if (packet.get() != 'c') {
00744 break;
00745 }
00746 if (packet.get() != 'c') {
00747 break;
00748 }
00749 if (packet.get() != 'e') {
00750 break;
00751 }
00752 if (packet.get() != 's') {
00753 break;
00754 }
00755 if (packet.get() != 's') {
00756 break;
00757 }
00758 if (packet.hasRemaining()) {
00759 break;
00760 }
00761 if (debugProtocol != null) {
00762 debugProtocol.debugProtocolWrite("recv addme_success");
00763 }
00764 processAddmeSuccess(packet);
00765 return;
00766 }
00767 break;
00768
00769 case 'q':
00770 if (packet.get() != 'u') {
00771 break;
00772 }
00773 if (packet.get() != 'e') {
00774 break;
00775 }
00776 if (packet.get() != 's') {
00777 break;
00778 }
00779 if (packet.get() != 't') {
00780 break;
00781 }
00782 if (packet.get() != ' ') {
00783 break;
00784 }
00785
00786 processAddQuest(packet);
00787 return;
00788
00789 case 's':
00790 if (packet.get() != 'p') {
00791 break;
00792 }
00793 if (packet.get() != 'e') {
00794 break;
00795 }
00796 if (packet.get() != 'l') {
00797 break;
00798 }
00799 if (packet.get() != 'l') {
00800 break;
00801 }
00802 if (packet.get() != ' ') {
00803 break;
00804 }
00805 processAddSpell(packet);
00806 return;
00807 }
00808 break;
00809
00810 case 'n':
00811 if (packet.get() != 'i') {
00812 break;
00813 }
00814 if (packet.get() != 'm') {
00815 break;
00816 }
00817 if (packet.get() != ' ') {
00818 break;
00819 }
00820 processAnim(packet);
00821 return;
00822 }
00823 break;
00824
00825 case 'c':
00826 if (packet.get() != 'o') {
00827 break;
00828 }
00829 if (packet.get() != 'm') {
00830 break;
00831 }
00832 if (packet.get() != 'c') {
00833 break;
00834 }
00835 if (packet.get() != ' ') {
00836 break;
00837 }
00838 processComc(packet);
00839 return;
00840
00841 case 'd':
00842 switch (packet.get()) {
00843 case 'e':
00844 if (packet.get() != 'l') {
00845 break;
00846 }
00847 switch (packet.get()) {
00848 case 'i':
00849 switch (packet.get()) {
00850 case 'n':
00851 if (packet.get() != 'v') {
00852 break;
00853 }
00854 if (packet.get() != ' ') {
00855 break;
00856 }
00857 processDelInv(packet);
00858 return;
00859
00860 case 't':
00861 if (packet.get() != 'e') {
00862 break;
00863 }
00864 if (packet.get() != 'm') {
00865 break;
00866 }
00867 if (packet.get() != ' ') {
00868 break;
00869 }
00870 processDelItem(packet);
00871 return;
00872 }
00873 break;
00874
00875 case 's':
00876 if (packet.get() != 'p') {
00877 break;
00878 }
00879 if (packet.get() != 'e') {
00880 break;
00881 }
00882 if (packet.get() != 'l') {
00883 break;
00884 }
00885 if (packet.get() != 'l') {
00886 break;
00887 }
00888 if (packet.get() != ' ') {
00889 break;
00890 }
00891 processDelSpell(packet);
00892 return;
00893 }
00894 break;
00895
00896 case 'r':
00897 if (packet.get() != 'a') {
00898 break;
00899 }
00900 if (packet.get() != 'w') {
00901 break;
00902 }
00903 switch (packet.get()) {
00904 case 'e':
00905 if (packet.get() != 'x') {
00906 break;
00907 }
00908 if (packet.get() != 't') {
00909 break;
00910 }
00911 if (packet.get() != 'i') {
00912 break;
00913 }
00914 if (packet.get() != 'n') {
00915 break;
00916 }
00917 if (packet.get() != 'f') {
00918 break;
00919 }
00920 if (packet.get() != 'o') {
00921 break;
00922 }
00923 if (packet.get() != ' ') {
00924 break;
00925 }
00926 processDrawExtInfo(packet);
00927 return;
00928
00929 case 'i':
00930 if (packet.get() != 'n') {
00931 break;
00932 }
00933 if (packet.get() != 'f') {
00934 break;
00935 }
00936 if (packet.get() != 'o') {
00937 break;
00938 }
00939 if (packet.get() != ' ') {
00940 break;
00941 }
00942 processDrawInfo(packet);
00943 return;
00944 }
00945 break;
00946 }
00947 break;
00948
00949 case 'E':
00950 if (packet.get() != 'x') {
00951 break;
00952 }
00953 if (packet.get() != 't') {
00954 break;
00955 }
00956 if (packet.get() != 'e') {
00957 break;
00958 }
00959 if (packet.get() != 'n') {
00960 break;
00961 }
00962 if (packet.get() != 'd') {
00963 break;
00964 }
00965 if (packet.get() != 'e') {
00966 break;
00967 }
00968 if (packet.get() != 'd') {
00969 break;
00970 }
00971 switch (packet.get()) {
00972 case 'I':
00973 if (packet.get() != 'n') {
00974 break;
00975 }
00976 if (packet.get() != 'f') {
00977 break;
00978 }
00979 if (packet.get() != 'o') {
00980 break;
00981 }
00982 if (packet.get() != 'S') {
00983 break;
00984 }
00985 if (packet.get() != 'e') {
00986 break;
00987 }
00988 if (packet.get() != 't') {
00989 break;
00990 }
00991 if (packet.get() != ' ') {
00992 break;
00993 }
00994 processExtendedInfoSet(packet);
00995 return;
00996
00997 case 'T':
00998 if (packet.get() != 'e') {
00999 break;
01000 }
01001 if (packet.get() != 'x') {
01002 break;
01003 }
01004 if (packet.get() != 't') {
01005 break;
01006 }
01007 if (packet.get() != 'S') {
01008 break;
01009 }
01010 if (packet.get() != 'e') {
01011 break;
01012 }
01013 if (packet.get() != 't') {
01014 break;
01015 }
01016 if (packet.get() != ' ') {
01017 break;
01018 }
01019 processExtendedTextSet(packet);
01020 return;
01021 }
01022 break;
01023
01024 case 'f':
01025 if (packet.get() != 'a') {
01026 break;
01027 }
01028 switch (packet.get()) {
01029 case 'c':
01030 if (packet.get() != 'e') {
01031 break;
01032 }
01033 if (packet.get() != '2') {
01034 break;
01035 }
01036 if (packet.get() != ' ') {
01037 break;
01038 }
01039 processFace2(packet);
01040 return;
01041
01042 case 'i':
01043 if (packet.get() != 'l') {
01044 break;
01045 }
01046 if (packet.get() != 'u') {
01047 break;
01048 }
01049 if (packet.get() != 'r') {
01050 break;
01051 }
01052 if (packet.get() != 'e') {
01053 break;
01054 }
01055 if (packet.get() != ' ') {
01056 break;
01057 }
01058 processFailure(packet);
01059 return;
01060 }
01061
01062 case 'g':
01063 if (packet.get() != 'o') {
01064 break;
01065 }
01066 if (packet.get() != 'o') {
01067 break;
01068 }
01069 if (packet.get() != 'd') {
01070 break;
01071 }
01072 if (packet.get() != 'b') {
01073 break;
01074 }
01075 if (packet.get() != 'y') {
01076 break;
01077 }
01078 if (packet.get() != 'e') {
01079 break;
01080 }
01081 if (packet.get() != ' ') {
01082 break;
01083 }
01084 processGoodbye(packet);
01085 return;
01086
01087 case 'i':
01088 switch (packet.get()) {
01089 case 'm':
01090 if (packet.get() != 'a') {
01091 break;
01092 }
01093 if (packet.get() != 'g') {
01094 break;
01095 }
01096 if (packet.get() != 'e') {
01097 break;
01098 }
01099 if (packet.get() != '2') {
01100 break;
01101 }
01102 if (packet.get() != ' ') {
01103 break;
01104 }
01105 processImage2(packet);
01106 return;
01107
01108 case 't':
01109 if (packet.get() != 'e') {
01110 break;
01111 }
01112 if (packet.get() != 'm') {
01113 break;
01114 }
01115 if (packet.get() != '2') {
01116 break;
01117 }
01118 if (packet.get() != ' ') {
01119 break;
01120 }
01121 processItem2(packet);
01122 return;
01123 }
01124 break;
01125
01126 case 'm':
01127 switch (packet.get()) {
01128 case 'a':
01129 switch (packet.get()) {
01130 case 'g':
01131 if (packet.get() != 'i') {
01132 break;
01133 }
01134 if (packet.get() != 'c') {
01135 break;
01136 }
01137 if (packet.get() != 'm') {
01138 break;
01139 }
01140 if (packet.get() != 'a') {
01141 break;
01142 }
01143 if (packet.get() != 'p') {
01144 break;
01145 }
01146 if (packet.get() != ' ') {
01147 break;
01148 }
01149 processMagicMap(packet);
01150 return;
01151
01152 case 'p':
01153 switch (packet.get()) {
01154 case '2':
01155 if (packet.get() != ' ') {
01156 break;
01157 }
01158 processMap2(packet);
01159 return;
01160
01161 case 'e':
01162 if (packet.get() != 'x') {
01163 break;
01164 }
01165 if (packet.get() != 't') {
01166 break;
01167 }
01168 if (packet.get() != 'e') {
01169 break;
01170 }
01171 if (packet.get() != 'n') {
01172 break;
01173 }
01174 if (packet.get() != 'd') {
01175 break;
01176 }
01177 if (packet.get() != 'e') {
01178 break;
01179 }
01180 if (packet.get() != 'd') {
01181 break;
01182 }
01183 if (packet.get() != ' ') {
01184 break;
01185 }
01186 processMapExtended(packet);
01187 return;
01188 }
01189 break;
01190 }
01191 break;
01192
01193 case 'u':
01194 if (packet.get() != 's') {
01195 break;
01196 }
01197 if (packet.get() != 'i') {
01198 break;
01199 }
01200 if (packet.get() != 'c') {
01201 break;
01202 }
01203 if (packet.get() != ' ') {
01204 break;
01205 }
01206 processMusic(packet);
01207 return;
01208 }
01209 break;
01210
01211 case 'n':
01212 if (packet.get() != 'e') {
01213 break;
01214 }
01215 if (packet.get() != 'w') {
01216 break;
01217 }
01218 if (packet.get() != 'm') {
01219 break;
01220 }
01221 if (packet.get() != 'a') {
01222 break;
01223 }
01224 if (packet.get() != 'p') {
01225 break;
01226 }
01227 processNewMap(packet);
01228 return;
01229
01230 case 'p':
01231 switch (packet.get()) {
01232 case 'i':
01233 if (packet.get() != 'c') {
01234 break;
01235 }
01236 if (packet.get() != 'k') {
01237 break;
01238 }
01239 if (packet.get() != 'u') {
01240 break;
01241 }
01242 if (packet.get() != 'p') {
01243 break;
01244 }
01245 if (packet.get() != ' ') {
01246 break;
01247 }
01248 processPickup(packet);
01249 return;
01250
01251 case 'l':
01252 if (packet.get() != 'a') {
01253 break;
01254 }
01255 if (packet.get() != 'y') {
01256 break;
01257 }
01258 if (packet.get() != 'e') {
01259 break;
01260 }
01261 if (packet.get() != 'r') {
01262 break;
01263 }
01264 if (packet.get() != ' ') {
01265 break;
01266 }
01267 processPlayer(packet);
01268 return;
01269 }
01270 break;
01271
01272 case 'q':
01273 if (packet.get() != 'u') {
01274 break;
01275 }
01276 if (packet.get() != 'e') {
01277 break;
01278 }
01279 if (packet.get() != 'r') {
01280 break;
01281 }
01282 if (packet.get() != 'y') {
01283 break;
01284 }
01285 if (packet.get() != ' ') {
01286 break;
01287 }
01288 processQuery(packet);
01289 return;
01290
01291 case 'r':
01292 if (packet.get() != 'e') {
01293 break;
01294 }
01295 if (packet.get() != 'p') {
01296 break;
01297 }
01298 if (packet.get() != 'l') {
01299 break;
01300 }
01301 if (packet.get() != 'y') {
01302 break;
01303 }
01304 if (packet.get() != 'i') {
01305 break;
01306 }
01307 if (packet.get() != 'n') {
01308 break;
01309 }
01310 if (packet.get() != 'f') {
01311 break;
01312 }
01313 if (packet.get() != 'o') {
01314 break;
01315 }
01316 if (packet.get() != ' ') {
01317 break;
01318 }
01319 processReplyInfo(packet);
01320 return;
01321
01322 case 's':
01323 switch (packet.get()) {
01324 case 'e':
01325 if (packet.get() != 't') {
01326 break;
01327 }
01328 if (packet.get() != 'u') {
01329 break;
01330 }
01331 if (packet.get() != 'p') {
01332 break;
01333 }
01334 if (packet.get() != ' ') {
01335 break;
01336 }
01337 processSetup(packet);
01338 return;
01339
01340 case 'm':
01341 if (packet.get() != 'o') {
01342 break;
01343 }
01344 if (packet.get() != 'o') {
01345 break;
01346 }
01347 if (packet.get() != 't') {
01348 break;
01349 }
01350 if (packet.get() != 'h') {
01351 break;
01352 }
01353 if (packet.get() != ' ') {
01354 break;
01355 }
01356 processSmooth(packet);
01357 return;
01358
01359 case 'o':
01360 if (packet.get() != 'u') {
01361 break;
01362 }
01363 if (packet.get() != 'n') {
01364 break;
01365 }
01366 if (packet.get() != 'd') {
01367 break;
01368 }
01369 switch (packet.get()) {
01370 case ' ':
01371 processSound(packet);
01372 return;
01373
01374 case '2':
01375 if (packet.get() != ' ') {
01376 break;
01377 }
01378 processSound2(packet);
01379 return;
01380 }
01381 break;
01382
01383 case 't':
01384 if (packet.get() != 'a') {
01385 break;
01386 }
01387 if (packet.get() != 't') {
01388 break;
01389 }
01390 if (packet.get() != 's') {
01391 break;
01392 }
01393 if (packet.get() != ' ') {
01394 break;
01395 }
01396 processStats(packet);
01397 return;
01398 }
01399 break;
01400
01401 case 't':
01402 if (packet.get() != 'i') {
01403 break;
01404 }
01405 if (packet.get() != 'c') {
01406 break;
01407 }
01408 if (packet.get() != 'k') {
01409 break;
01410 }
01411 if (packet.get() != ' ') {
01412 break;
01413 }
01414 processTick(packet);
01415 return;
01416
01417 case 'u':
01418 if (packet.get() != 'p') {
01419 break;
01420 }
01421 if (packet.get() != 'd') {
01422 break;
01423 }
01424 switch (packet.get()) {
01425 case 'i':
01426 if (packet.get() != 't') {
01427 break;
01428 }
01429 if (packet.get() != 'e') {
01430 break;
01431 }
01432 if (packet.get() != 'm') {
01433 break;
01434 }
01435 if (packet.get() != ' ') {
01436 break;
01437 }
01438 processUpdItem(packet);
01439 return;
01440
01441 case 'q':
01442 if (packet.get() != 'u') {
01443 break;
01444 }
01445 if (packet.get() != 'e') {
01446 break;
01447 }
01448 if (packet.get() != 's') {
01449 break;
01450 }
01451 if (packet.get() != 't') {
01452 break;
01453 }
01454 if (packet.get() != ' ') {
01455 break;
01456 }
01457 processUpdQuest(packet);
01458 return;
01459
01460 case 's':
01461 if (packet.get() != 'p') {
01462 break;
01463 }
01464 if (packet.get() != 'e') {
01465 break;
01466 }
01467 if (packet.get() != 'l') {
01468 break;
01469 }
01470 if (packet.get() != 'l') {
01471 break;
01472 }
01473 if (packet.get() != ' ') {
01474 break;
01475 }
01476 processUpdSpell(packet);
01477 return;
01478 }
01479 break;
01480
01481 case 'v':
01482 if (packet.get() != 'e') {
01483 break;
01484 }
01485 if (packet.get() != 'r') {
01486 break;
01487 }
01488 if (packet.get() != 's') {
01489 break;
01490 }
01491 if (packet.get() != 'i') {
01492 break;
01493 }
01494 if (packet.get() != 'o') {
01495 break;
01496 }
01497 if (packet.get() != 'n') {
01498 break;
01499 }
01500 if (packet.get() != ' ') {
01501 break;
01502 }
01503 processVersion(packet);
01504 return;
01505 }
01506 } catch (final IllegalArgumentException ex) {
01507 if (debugProtocol != null) {
01508 debugProtocol.debugProtocolWrite("IllegalArgumentException while command parsing: "+ex+"\n"+hexDump(packet), ex);
01509 }
01510 } catch (final BufferUnderflowException ex) {
01511 if (debugProtocol != null) {
01512 debugProtocol.debugProtocolWrite("BufferUnderflowException while command parsing: "+ex+"\n"+hexDump(packet), ex);
01513 }
01514 } catch (final ArrayIndexOutOfBoundsException ex) {
01515 if (debugProtocol != null) {
01516 debugProtocol.debugProtocolWrite("ArrayIndexOutOfBoundsException while command parsing: "+ex+"\n"+hexDump(packet), ex);
01517 }
01518 } catch (final StringIndexOutOfBoundsException ex) {
01519 if (debugProtocol != null) {
01520 debugProtocol.debugProtocolWrite("StringIndexOutOfBoundsException while command parsing: "+ex+"\n"+hexDump(packet), ex);
01521 }
01522 } catch (final UnknownCommandException ex) {
01523 if (debugProtocol != null) {
01524 debugProtocol.debugProtocolWrite("UnknownCommandException while command parsing: "+ex+"\n"+hexDump(packet), ex);
01525 }
01526 throw ex;
01527 }
01528
01529 packet.position(0);
01530 final String command = extractCommand(packet);
01531 if (debugProtocol != null) {
01532 debugProtocol.debugProtocolWrite("recv invalid command: "+command+"\n"+hexDump(packet));
01533 }
01534 throw new UnknownCommandException("Cannot parse command: "+command);
01535 }
01536
01544 private void cmdMap2Coordinate(@NotNull final ByteBuffer packet, final int x, final int y) throws UnknownCommandException {
01545 while (true) {
01546 final int lenType = getInt1(packet);
01547 if (lenType == 0xFF) {
01548 break;
01549 }
01550
01551 final int len = (lenType>>5)&7;
01552 final int type = lenType&31;
01553 switch (type) {
01554 case Map2.COORD_CLEAR_SPACE:
01555 cmdMap2CoordinateClearSpace(x, y, len);
01556 break;
01557
01558 case Map2.COORD_DARKNESS:
01559 cmdMap2CoordinateDarkness(packet, x, y, len);
01560 break;
01561
01562 case Map2.COORD_LAYER0:
01563 case Map2.COORD_LAYER1:
01564 case Map2.COORD_LAYER2:
01565 case Map2.COORD_LAYER3:
01566 case Map2.COORD_LAYER4:
01567 case Map2.COORD_LAYER5:
01568 case Map2.COORD_LAYER6:
01569 case Map2.COORD_LAYER7:
01570 case Map2.COORD_LAYER8:
01571 case Map2.COORD_LAYER9:
01572 cmdMap2CoordinateLayer(packet, x, y, len, type-Map2.COORD_LAYER0);
01573 break;
01574 }
01575 }
01576 }
01577
01586 private void cmdMap2CoordinateClearSpace(final int x, final int y, final int len) throws UnknownCommandException {
01587 if (len != 0) {
01588 throw new UnknownCommandException("map2 command contains clear command with length "+len);
01589 }
01590 if (debugProtocol != null) {
01591 debugProtocol.debugProtocolWrite("recv map2 "+x+"/"+y+" clear");
01592 }
01593 fireMapClear(x, y);
01594 }
01595
01604 private void cmdMap2CoordinateDarkness(@NotNull final ByteBuffer packet, final int x, final int y, final int len) throws UnknownCommandException {
01605 if (len != 1) {
01606 throw new UnknownCommandException("map2 command contains darkness command with length "+len);
01607 }
01608 final int darkness = getInt1(packet);
01609 if (debugProtocol != null) {
01610 debugProtocol.debugProtocolWrite("recv map2 "+x+"/"+y+" darkness="+darkness);
01611 }
01612 fireMapDarkness(x, y, darkness);
01613 }
01614
01624 private void cmdMap2CoordinateLayer(@NotNull final ByteBuffer packet, final int x, final int y, final int len, final int layer) throws UnknownCommandException {
01625 if (len < 2) {
01626 throw new UnknownCommandException("map2 command contains image command with length "+len);
01627 }
01628 final Location location = new Location(x, y, layer);
01629 final int face = getInt2(packet);
01630 if ((face&Map2.FACE_ANIMATION) == 0) {
01631 if (debugProtocol != null) {
01632 debugProtocol.debugProtocolWrite("recv map2 "+location+" face="+face);
01633 }
01634 fireMapFace(location, face);
01635 } else {
01636 if (debugProtocol != null) {
01637 debugProtocol.debugProtocolWrite("recv map2 "+location+" anim="+(face&Map2.ANIM_MASK)+" type="+((face>>Map2.ANIM_TYPE_SHIFT)&Map2.ANIM_TYPE_MASK));
01638 }
01639 fireMapAnimation(location, face&Map2.ANIM_MASK, (face>>Map2.ANIM_TYPE_SHIFT)&Map2.ANIM_TYPE_MASK);
01640 }
01641 if (len == 3) {
01642 cmdMap2CoordinateLayer3(packet, location, face);
01643 } else if (len == 4) {
01644 cmdMap2CoordinateLayer4(packet, location, face);
01645 } else if (len != 2) {
01646 if (debugProtocol != null) {
01647 debugProtocol.debugProtocolWrite("recv map2 "+x+"/"+y+"/"+layer+" <invalid>");
01648 }
01649 throw new UnknownCommandException("map2 command contains image command with length "+len);
01650 }
01651 }
01652
01661 private void cmdMap2CoordinateLayer3(@NotNull final ByteBuffer packet, @NotNull final Location location, final int face) throws UnknownCommandException {
01662 if (face == 0) {
01663 throw new UnknownCommandException("map2 command contains smoothing or animation information for empty face");
01664 }
01665
01666 if ((face&Map2.FACE_ANIMATION) == 0) {
01667 final int smooth = getInt1(packet);
01668 if (debugProtocol != null) {
01669 debugProtocol.debugProtocolWrite("recv map2 "+location+" smooth="+smooth);
01670 }
01671 fireMapSmooth(location, smooth);
01672 } else {
01673 final int animSpeed = getInt1(packet);
01674 if (debugProtocol != null) {
01675 debugProtocol.debugProtocolWrite("recv map2 "+location+" anim_speed="+animSpeed);
01676 }
01677 fireMapAnimationSpeed(location, animSpeed);
01678 }
01679 }
01680
01689 private void cmdMap2CoordinateLayer4(@NotNull final ByteBuffer packet, @NotNull final Location location, final int face) throws UnknownCommandException {
01690 if (face == 0) {
01691 throw new UnknownCommandException("map2 command contains smoothing or animation information for empty face");
01692 }
01693
01694 final int animSpeed = getInt1(packet);
01695 if (debugProtocol != null) {
01696 debugProtocol.debugProtocolWrite("recv map2 "+location+" anim_speed="+animSpeed);
01697 }
01698 fireMapAnimationSpeed(location, animSpeed);
01699
01700 final int smooth = getInt1(packet);
01701 if (debugProtocol != null) {
01702 debugProtocol.debugProtocolWrite("recv map2 "+location+" smooth="+smooth);
01703 }
01704 fireMapSmooth(location, smooth);
01705 }
01706
01712 private void negotiateMapSize(final int mapWidth, final int mapHeight) {
01713 if (debugProtocol != null) {
01714 debugProtocol.debugProtocolWrite("negotiateMapSize: "+mapWidth+"x"+mapHeight);
01715 }
01716
01717 if (clientSocketState == ClientSocketState.CONNECTING || clientSocketState == ClientSocketState.VERSION || clientSocketState == ClientSocketState.CONNECT_FAILED) {
01718 if (debugProtocol != null) {
01719 debugProtocol.debugProtocolWrite("negotiateMapSize: clientSocketState="+clientSocketState+", ignoring");
01720 }
01721 return;
01722 }
01723 if (pendingMapWidth != 0 || pendingMapHeight != 0) {
01724 if (debugProtocol != null) {
01725 debugProtocol.debugProtocolWrite("negotiateMapSize: already negotiating, ignoring");
01726 }
01727 return;
01728 }
01729 if (currentMapWidth == mapWidth && currentMapHeight == mapHeight) {
01730 if (debugProtocol != null) {
01731 debugProtocol.debugProtocolWrite("negotiateMapSize: same as current map size, ignoring");
01732 }
01733 return;
01734 }
01735 pendingMapWidth = mapWidth;
01736 pendingMapHeight = mapHeight;
01737 sendSetup("mapsize "+pendingMapWidth+"x"+pendingMapHeight);
01738 }
01739
01744 private void negotiateNumLookObjects(final int numLookObjects) {
01745 if (debugProtocol != null) {
01746 debugProtocol.debugProtocolWrite("negotiateNumLookObjects: "+numLookObjects);
01747 }
01748
01749 if (clientSocketState == ClientSocketState.CONNECTING || clientSocketState == ClientSocketState.VERSION || clientSocketState == ClientSocketState.CONNECT_FAILED) {
01750 if (debugProtocol != null) {
01751 debugProtocol.debugProtocolWrite("negotiateNumLookObjects: clientSocketState="+clientSocketState+", ignoring");
01752 }
01753 return;
01754 }
01755 if (pendingNumLookObjects != 0) {
01756 if (debugProtocol != null) {
01757 debugProtocol.debugProtocolWrite("negotiateNumLookObjects: already negotiating pending_num_look_objects="+pendingNumLookObjects+", ignoring");
01758 }
01759 return;
01760 }
01761 if (currentNumLookObjects == numLookObjects) {
01762 if (debugProtocol != null) {
01763 debugProtocol.debugProtocolWrite("negotiateNumLookObjects: unchanged from num_look_objects="+currentNumLookObjects+", ignoring");
01764 }
01765 return;
01766 }
01767 pendingNumLookObjects = numLookObjects;
01768 if (debugProtocol != null) {
01769 debugProtocol.debugProtocolWrite("negotateNumLookObjects: pending_num_look_objects="+pendingNumLookObjects+", sending setup command");
01770 }
01771 sendSetup("num_look_objects "+pendingNumLookObjects);
01772 }
01773
01780 private void cmdReplyinfo(@NotNull final String infoType, final ByteBuffer packet) throws IOException {
01781 if (infoType.equals("image_info")) {
01782 processImageInfoReplyinfo(packet);
01783 } else if (infoType.equals("skill_info")) {
01784 processSkillInfoReplyinfo(packet);
01785 } else if (infoType.equals("exp_table")) {
01786 processExpTableReplyinfo(packet);
01787 } else {
01788 System.err.println("Ignoring unexpected replyinfo type '"+infoType+"'.");
01789 }
01790 }
01791
01797 private static void processImageInfoReplyinfo(@NotNull final ByteBuffer packet) throws IOException {
01798 final byte[] data = new byte[packet.remaining()];
01799 packet.get(data);
01800 final ByteArrayInputStream is = new ByteArrayInputStream(data);
01801 try {
01802 final InputStreamReader isr = new InputStreamReader(is);
01803 try {
01804 final BufferedReader d = new BufferedReader(isr);
01805 try {
01806 final String info = d.readLine();
01807 if (info == null) {
01808 throw new IOException("Truncated parameter in image_info");
01809 }
01810
01811 final int nrPics = Integer.parseInt(info);
01812
01813 } finally {
01814 d.close();
01815 }
01816 } finally {
01817 isr.close();
01818 }
01819 } finally {
01820 is.close();
01821 }
01822 }
01823
01829 private void processSkillInfoReplyinfo(@NotNull final ByteBuffer packet) throws IOException {
01830 model.getSkillSet().clearSkills();
01831 final byte[] data = new byte[packet.remaining()];
01832 packet.get(data);
01833 final ByteArrayInputStream is = new ByteArrayInputStream(data);
01834 try {
01835 final InputStreamReader isr = new InputStreamReader(is);
01836 try {
01837 final BufferedReader d = new BufferedReader(isr);
01838 try {
01839 while (true) {
01840 final String r = d.readLine();
01841 if (r == null) {
01842 break;
01843 }
01844
01845 final String[] sk = PATTERN_DOT.split(r, 2);
01846 if (sk.length != 2) {
01847 System.err.println("Ignoring skill definition for invalid skill: "+r+".");
01848 continue;
01849 }
01850
01851 final int skillId;
01852 try {
01853 skillId = Integer.parseInt(sk[0]);
01854 } catch (final NumberFormatException ignored) {
01855 System.err.println("Ignoring skill definition for invalid skill: "+r+".");
01856 continue;
01857 }
01858
01859 if (skillId < Stats.CS_STAT_SKILLINFO || skillId >= Stats.CS_STAT_SKILLINFO+Stats.CS_NUM_SKILLS) {
01860 System.err.println("Ignoring skill definition for invalid skill id "+skillId+": "+r+".");
01861 continue;
01862 }
01863
01864 model.getSkillSet().addSkill(skillId, sk[1]);
01865 }
01866 } finally {
01867 d.close();
01868 }
01869 } finally {
01870 isr.close();
01871 }
01872 } finally {
01873 is.close();
01874 }
01875 }
01876
01881 private void processExpTableReplyinfo(@NotNull final ByteBuffer packet) {
01882 final int numLevels = getInt2(packet);
01883 final long[] expTable = new long[numLevels];
01884 for (int level = 1; level < numLevels; level++) {
01885 expTable[level] = getInt8(packet);
01886 }
01887 if (packet.hasRemaining()) {
01888 System.err.println("Ignoring excess data at end of exp_table");
01889 }
01890
01891 model.getExperienceTable().setExpTable(expTable);
01892
01893 if (loginMethod == 0) {
01894 setClientSocketState(ClientSocketState.REQUESTINFO, ClientSocketState.ADDME);
01895 sendAddme();
01896 } else {
01897 setClientSocketState(ClientSocketState.REQUESTINFO, ClientSocketState.ACCOUNT_INFO);
01898 fireManageAccount();
01899 }
01900 }
01901
01907 private void processAccountPlayers(@NotNull final ByteBuffer packet) throws UnknownCommandException {
01908 final int args = packet.position();
01909 fireStartAccountList(accountName);
01910
01911
01912 int count = getInt1(packet);
01913 while (count > 0) {
01914 String name = "";
01915 String cClass = "";
01916 String race = "";
01917 String face = "";
01918 String party = "";
01919 String map = "";
01920 int level = 0;
01921 int faceNumber = 0;
01922
01923 while (packet.hasRemaining()) {
01924 final int len = getInt1(packet);
01925
01926 if (len == 0) {
01927 if (debugProtocol != null) {
01928 debugProtocol.debugProtocolWrite("recv accountplayers entry");
01929 }
01930
01931 count--;
01932
01933 fireAddAccount(name, cClass, race, face, party, map, level, faceNumber);
01934 break;
01935 }
01936
01937 final int type = getInt1(packet);
01938
01939 switch (type) {
01940 case ACL_NAME:
01941 name = getString(packet, len-1);
01942 if (debugProtocol != null) {
01943 debugProtocol.debugProtocolWrite("recv accountplayers name="+name);
01944 }
01945 break;
01946
01947 case ACL_CLASS:
01948 cClass = getString(packet, len-1);
01949 if (debugProtocol != null) {
01950 debugProtocol.debugProtocolWrite("recv accountplayers class="+cClass);
01951 }
01952 break;
01953
01954 case ACL_RACE:
01955 race = getString(packet, len-1);
01956 if (debugProtocol != null) {
01957 debugProtocol.debugProtocolWrite("recv accountplayers race="+race);
01958 }
01959 break;
01960
01961 case ACL_LEVEL:
01962 level = getInt2(packet);
01963 if (debugProtocol != null) {
01964 debugProtocol.debugProtocolWrite("recv accountplayers level="+level);
01965 }
01966 break;
01967
01968 case ACL_FACE:
01969 face = getString(packet, len-1);
01970 if (debugProtocol != null) {
01971 debugProtocol.debugProtocolWrite("recv accountplayers face="+face);
01972 }
01973 break;
01974
01975 case ACL_PARTY:
01976 party = getString(packet, len-1);
01977 if (debugProtocol != null) {
01978 debugProtocol.debugProtocolWrite("recv accountplayers party="+party);
01979 }
01980 break;
01981
01982 case ACL_MAP:
01983 map = getString(packet, len-1);
01984 if (debugProtocol != null) {
01985 debugProtocol.debugProtocolWrite("recv accountplayers map="+map);
01986 }
01987 break;
01988
01989 case ACL_FACE_NUM:
01990 faceNumber = getInt2(packet);
01991 if (debugProtocol != null) {
01992 debugProtocol.debugProtocolWrite("recv accountplayers face="+faceNumber);
01993 }
01994 break;
01995
01996 default:
01997
01998 if (debugProtocol != null) {
01999 debugProtocol.debugProtocolWrite("recv accountplayers unknown="+type);
02000 }
02001 packet.position(packet.position()+len-1);
02002 break;
02003 }
02004 }
02005 }
02006 if (packet.hasRemaining()) {
02007 throw new UnknownCommandException("invalid accountplayers reply, pos="+packet.position());
02008 }
02009
02010 fireEndAccountList();
02011
02012 packet.reset();
02013 notifyPacketWatcherListenersMixed(packet, args);
02014 }
02015
02020 private void processAddmeFailed(@NotNull final ByteBuffer packet) {
02021 final int args = packet.position();
02022
02023 notifyPacketWatcherListenersNoData(packet, args);
02024 }
02025
02030 private void processAddmeSuccess(@NotNull final ByteBuffer packet) {
02031 final int args = packet.position();
02032
02033 if (clientSocketState != ClientSocketState.CONNECTED) {
02034 if (clientSocketState == ClientSocketState.ADDME) {
02035
02036 setClientSocketState(ClientSocketState.ADDME, ClientSocketState.CONNECTED);
02037 } else if (clientSocketState == ClientSocketState.ACCOUNT_INFO) {
02038 fireStartPlaying();
02039 setClientSocketState(ClientSocketState.ACCOUNT_INFO, ClientSocketState.CONNECTED);
02040 negotiateNumLookObjects(preferredNumLookObjects);
02041 }
02042 negotiateMapSize(preferredMapWidth, preferredMapHeight);
02043 }
02044
02045 notifyPacketWatcherListenersNoData(packet, args);
02046 }
02047
02052 private void processAddQuest(@NotNull final ByteBuffer packet) {
02053 final int args = packet.position();
02054 while (packet.hasRemaining()) {
02055 final int code = getInt4(packet);
02056 final int titleLength = getInt2(packet);
02057 final String title = getString(packet, titleLength);
02058 final int face = getInt4(packet);
02059 final int replay = getInt1(packet);
02060 final int parent = getInt4(packet);
02061 final int end = getInt1(packet);
02062 final int stepLength = getInt2(packet);
02063 final String step = stepLength > 0 ? getString(packet, stepLength) : "";
02064
02065 if (debugProtocol != null) {
02066 debugProtocol.debugProtocolWrite("recv addquest code="+code+" title="+title+" face="+face+"replay="+replay+" end="+end+" desc="+step);
02067 }
02068 model.getQuestsManager().addQuest(code, title, face, replay == 1, parent, end == 1, step);
02069 }
02070 notifyPacketWatcherListenersMixed(packet, args);
02071 }
02072
02077 private void processAddSpell(@NotNull final ByteBuffer packet) {
02078 final int args = packet.position();
02079 while (packet.hasRemaining()) {
02080 final int tag = getInt4(packet);
02081 final int level = getInt2(packet);
02082 final int castingTime = getInt2(packet);
02083 final int mana = getInt2(packet);
02084 final int grace = getInt2(packet);
02085 final int damage = getInt2(packet);
02086 final int skill = getInt1(packet);
02087 final int path = getInt4(packet);
02088 final int face = getInt4(packet);
02089 final int nameLength = getInt1(packet);
02090 final String name = getString(packet, nameLength);
02091 final int messageLength = getInt2(packet);
02092 final String message = getString(packet, messageLength);
02093 if (debugProtocol != null) {
02094 debugProtocol.debugProtocolWrite("recv addspell tag="+tag+" lvl="+level+" time="+castingTime+" sp="+mana+" gr="+grace+" dam="+damage+" skill="+skill+" path="+path+" face="+face+" name="+name+" msg="+message);
02095 }
02096 model.getSpellsManager().addSpell(tag, level, castingTime, mana, grace, damage, skill, path, face, name, message);
02097 }
02098 notifyPacketWatcherListenersMixed(packet, args);
02099 }
02100
02106 private void processAnim(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02107 final int args = packet.position();
02108 final int num = getInt2(packet);
02109 final int flags = getInt2(packet);
02110 final int[] faces = new int[packet.remaining()/2];
02111 if (faces.length <= 0) {
02112 throw new UnknownCommandException("no faces in anim command");
02113 }
02114 for (int i = 0; i < faces.length; i++) {
02115 faces[i] = getInt2(packet);
02116 }
02117 if (packet.hasRemaining()) {
02118 throw new UnknownCommandException("excess data at end of anim command");
02119 }
02120 if (debugProtocol != null) {
02121 debugProtocol.debugProtocolWrite("recv anim num="+num+" flags="+flags+" faces="+Arrays.toString(faces));
02122 }
02123 if ((num&~0x1FFF) != 0) {
02124 throw new UnknownCommandException("invalid animation id "+num);
02125 }
02126 fireAddAnimation(num&0x1FFF, flags, faces);
02127 notifyPacketWatcherListenersShortArray(packet, args);
02128 }
02129
02135 private void processComc(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02136 final int args = packet.position();
02137 final int packetNo = getInt2(packet);
02138 final int time = getInt4(packet);
02139 if (packet.hasRemaining()) {
02140 throw new UnknownCommandException("excess data at end of comc command");
02141 }
02142 if (debugProtocol != null) {
02143 debugProtocol.debugProtocolWrite("recv comc no="+packetNo+" time="+time);
02144 }
02145 fireCommandComcReceived(packetNo, time);
02146 notifyPacketWatcherListenersShortInt(packet, args);
02147 }
02148
02154 private void processDelInv(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02155 final int args = packet.position();
02156 int tag = 0;
02157 do {
02158 tag = tag*10+parseDigit(packet.get());
02159 } while (packet.hasRemaining());
02160 if (packet.hasRemaining()) {
02161 throw new UnknownCommandException("excess data at end of delinv command");
02162 }
02163 if (debugProtocol != null) {
02164 debugProtocol.debugProtocolWrite("recv delinv tag="+tag);
02165 }
02166 fireDelinvReceived(tag);
02167 notifyPacketWatcherListenersAscii(packet, args);
02168 }
02169
02175 private void processDelItem(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02176 final int args = packet.position();
02177 final int[] tags = new int[packet.remaining()/4];
02178 for (int i = 0; i < tags.length; i++) {
02179 tags[i] = getInt4(packet);
02180 }
02181 if (packet.hasRemaining()) {
02182 throw new UnknownCommandException("excess data at end of delitem command");
02183 }
02184 if (debugProtocol != null) {
02185 debugProtocol.debugProtocolWrite("recv delitem tags="+Arrays.toString(tags));
02186 }
02187 fireDelitemReceived(tags);
02188 notifyPacketWatcherListenersIntArray(packet, args);
02189 }
02190
02196 private void processDelSpell(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02197 final int args = packet.position();
02198 final int tag = getInt4(packet);
02199 if (packet.hasRemaining()) {
02200 throw new UnknownCommandException("excess data at end of delspell command");
02201 }
02202 if (debugProtocol != null) {
02203 debugProtocol.debugProtocolWrite("recv delspell tag="+tag);
02204 }
02205 model.getSpellsManager().deleteSpell(tag);
02206 notifyPacketWatcherListenersIntArray(packet, args);
02207 }
02208
02214 private void processDrawExtInfo(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02215 final int args = packet.position();
02216 int color = 0;
02217 do {
02218 color = color*10+parseDigit(packet.get());
02219 } while (packet.get(packet.position()) != ' ');
02220 packet.get();
02221
02222 int type = 0;
02223 do {
02224 type = type*10+parseDigit(packet.get());
02225 } while (packet.get(packet.position()) != ' ');
02226 packet.get();
02227
02228 int subtype = 0;
02229 do {
02230 subtype = subtype*10+parseDigit(packet.get());
02231 } while (packet.get(packet.position()) != ' ');
02232 packet.get();
02233
02234 final String message = getString(packet, packet.remaining());
02235
02236 if (debugProtocol != null) {
02237 debugProtocol.debugProtocolWrite("recv drawextinfo color="+color+" type="+type+"/"+subtype+" msg="+message);
02238 }
02239
02240 drawextinfo(color, type, subtype, message);
02241 notifyPacketWatcherListenersAscii(packet, args);
02242 }
02243
02249 private void processDrawInfo(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02250 final int args = packet.position();
02251 int color = 0;
02252 do {
02253 color = color*10+parseDigit(packet.get());
02254 } while (packet.get(packet.position()) != ' ');
02255 packet.get();
02256
02257 final String message = getString(packet, packet.remaining());
02258
02259 if (debugProtocol != null) {
02260 debugProtocol.debugProtocolWrite("recv drawinfo color="+color+" msg="+message);
02261 }
02262
02263 drawInfo(message, color);
02264 notifyPacketWatcherListenersAscii(packet, args);
02265 }
02266
02271 private void processExtendedInfoSet(@NotNull final ByteBuffer packet) {
02272 final int args = packet.position();
02273 do {
02274 final int startPos = packet.position();
02275 while (packet.hasRemaining() && packet.get(packet.position()) != ' ') {
02276 packet.get();
02277 }
02278 final String string = newString(packet, startPos, packet.position()-startPos);
02279 packet.get();
02280 if (debugProtocol != null) {
02281 debugProtocol.debugProtocolWrite("recv ExtendedInfoSet "+string);
02282 }
02283
02284 } while (packet.hasRemaining());
02285 notifyPacketWatcherListenersNoData(packet, args);
02286 }
02287
02292 private void processExtendedTextSet(@NotNull final ByteBuffer packet) {
02293 final int args = packet.position();
02294 while (true) {
02295 final int startPos = packet.position();
02296 while (packet.hasRemaining() && packet.get(packet.position()) != ' ') {
02297 packet.get();
02298 }
02299 final String type = newString(packet, startPos, packet.position()-startPos);
02300 if (debugProtocol != null) {
02301 debugProtocol.debugProtocolWrite("recv ExtendedTextSet "+type);
02302 }
02303
02304 if (!packet.hasRemaining()) {
02305 break;
02306 }
02307 packet.get();
02308 }
02309 notifyPacketWatcherListenersNoData(packet, args);
02310 }
02311
02316 private void processFace2(@NotNull final ByteBuffer packet) {
02317 final int args = packet.position();
02318 final int faceNum = getInt2(packet);
02319 final int faceSetNum = getInt1(packet);
02320 final int faceChecksum = getInt4(packet);
02321 final String faceName = getString(packet, packet.remaining()).intern();
02322 if (debugProtocol != null) {
02323 debugProtocol.debugProtocolWrite("recv face2 num="+faceNum+" set="+faceSetNum+" checksum="+faceChecksum+" name="+faceName);
02324 }
02325 fireFaceReceived(faceNum, faceSetNum, faceChecksum, faceName);
02326 notifyPacketWatcherListenersMixed(packet, args);
02327 }
02328
02333 private void processFailure(@NotNull final ByteBuffer packet) {
02334 final String full = getString(packet, packet.remaining());
02335 final String command;
02336 final String message;
02337 final int idx = full.indexOf(' ');
02338 if (idx == -1) {
02339 command = full;
02340 message = "";
02341 } else {
02342 command = full.substring(0, idx);
02343 message = full.substring(idx+1);
02344 }
02345 if (debugProtocol != null) {
02346 debugProtocol.debugProtocolWrite("recv failure command="+command+" message="+message);
02347 }
02348
02349 fireFailure(command, message);
02350 }
02351
02357 private void processGoodbye(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02358 final int args = packet.position();
02359 if (packet.hasRemaining()) {
02360 throw new UnknownCommandException("excess data at end of goodbye command");
02361 }
02362 if (debugProtocol != null) {
02363 debugProtocol.debugProtocolWrite("recv goodbye");
02364 }
02365
02366 notifyPacketWatcherListenersNoData(packet, args);
02367 }
02368
02374 private void processImage2(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02375 final int args = packet.position();
02376 final int faceNum = getInt4(packet);
02377 final int faceSetNum = getInt1(packet);
02378 final int len = getInt4(packet);
02379 if (packet.remaining() != len) {
02380 throw new UnknownCommandException("excess data at end of image2 command");
02381 }
02382 final int faceDataPosition = packet.position();
02383 if (debugProtocol != null) {
02384 debugProtocol.debugProtocolWrite("recv image2 face="+faceNum+" set="+faceSetNum+" len="+len);
02385 }
02386 packet.position(faceDataPosition);
02387 model.getAskfaceFaceQueue().faceReceived(faceNum, faceSetNum, packet);
02388 notifyPacketWatcherListenersMixed(packet, args);
02389 }
02390
02396 private void processItem2(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02397 final int args = packet.position();
02398 final int location = getInt4(packet);
02399 while (packet.hasRemaining()) {
02400 final int tag = getInt4(packet);
02401 final int flags = getInt4(packet);
02402 final int weight = getInt4(packet);
02403 final int faceNum = getInt4(packet);
02404 final int nameLength = getInt1(packet);
02405 final String[] names = getString(packet, nameLength).split("\0", 2);
02406 final String name = names[0].intern();
02407 final String namePl = names.length < 2 ? name : names[1].intern();
02408 final int anim = getInt2(packet);
02409 final int animSpeed = getInt1(packet);
02410 final int nrof = getInt4(packet);
02411 final int type = getInt2(packet);
02412 if (debugProtocol != null) {
02413 debugProtocol.debugProtocolWrite("recv item2 location="+location+" tag="+tag+" flags="+flags+" weight="+weight+" face="+faceNum+" name="+name+" name_pl="+namePl+" anim="+anim+" anim_speed="+animSpeed+" nrof="+nrof+" type="+type);
02414 }
02415 fireAddItemReceived(location, tag, flags, weight, faceNum, name, namePl, anim, animSpeed, nrof, type);
02416 }
02417 if (packet.hasRemaining()) {
02418 throw new UnknownCommandException("excess data at end of item2 command");
02419 }
02420 notifyPacketWatcherListenersMixed(packet, args);
02421 }
02422
02428 private void processMagicMap(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02429 final int args = packet.position();
02430
02431 final boolean widthSign = packet.get(packet.position()) == '-';
02432 if (widthSign) {
02433 packet.get();
02434 }
02435 int width = 0;
02436 do {
02437 width = width*10+parseDigit(packet.get());
02438 } while (packet.get(packet.position()) != ' ');
02439 packet.get();
02440 if (widthSign) {
02441 width = -width;
02442 }
02443
02444 final boolean heightSign = packet.get(packet.position()) == '-';
02445 if (heightSign) {
02446 packet.get();
02447 }
02448 int height = 0;
02449 do {
02450 height = height*10+parseDigit(packet.get());
02451 } while (packet.get(packet.position()) != ' ');
02452 packet.get();
02453 if (heightSign) {
02454 height = -height;
02455 }
02456
02457 final boolean pxSign = packet.get(packet.position()) == '-';
02458 if (pxSign) {
02459 packet.get();
02460 }
02461 int px = 0;
02462 do {
02463 px = px*10+parseDigit(packet.get());
02464 } while (packet.get(packet.position()) != ' ');
02465 packet.get();
02466 if (pxSign) {
02467 px = -px;
02468 }
02469
02470 final boolean pySign = packet.get(packet.position()) == '-';
02471 if (pySign) {
02472 packet.get();
02473 }
02474 int py = 0;
02475 do {
02476 py = py*10+parseDigit(packet.get());
02477 } while (packet.get(packet.position()) != ' ');
02478 packet.get();
02479 if (pySign) {
02480 py = -py;
02481 }
02482
02483 if (debugProtocol != null) {
02484 debugProtocol.debugProtocolWrite("recv magicmap size="+width+"x"+height+" player="+px+"/"+py+" len="+packet.remaining());
02485 }
02486
02487 if (packet.remaining() != width*height) {
02488 throw new UnknownCommandException("invalid magicmap command");
02489 }
02490
02491 final byte[][] data = new byte[height][width];
02492 for (int y = 0; y < height; y++) {
02493 packet.get(data[y]);
02494 }
02495 fireMagicMap(-px+(currentMapWidth-1)/2, -py+(currentMapHeight-1)/2, data);
02496 notifyPacketWatcherListenersMixed(packet, args);
02497 }
02498
02504 private void processMap2(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02505 final int args = packet.position();
02506 if (debugProtocol != null) {
02507 debugProtocol.debugProtocolWrite("recv map2 begin");
02508 }
02509 if (crossfireUpdateMapListener != null) {
02510 synchronized (crossfireUpdateMapListener.mapBegin()) {
02511 while (packet.hasRemaining()) {
02512 final int coord = getInt2(packet);
02513 final int x = ((coord>>10)&0x3F)-Map2.COORD_OFFSET;
02514 final int y = ((coord>>4)&0x3F)-Map2.COORD_OFFSET;
02515 final int coordType = coord&0xF;
02516
02517 switch (coordType) {
02518 case Map2.TYPE_COORDINATE:
02519 cmdMap2Coordinate(packet, x, y);
02520 break;
02521
02522 case Map2.TYPE_SCROLL:
02523 if (debugProtocol != null) {
02524 debugProtocol.debugProtocolWrite("recv map2 "+x+"/"+y+" scroll");
02525 }
02526 assert crossfireUpdateMapListener != null;
02527 crossfireUpdateMapListener.mapScroll(x, y);
02528 break;
02529
02530 default:
02531 if (debugProtocol != null) {
02532 debugProtocol.debugProtocolWrite("recv map2 "+x+"/"+y+" <invalid>");
02533 }
02534 throw new UnknownCommandException("map2 command contains unexpected coordinate type "+coordType);
02535 }
02536 }
02537 assert crossfireUpdateMapListener != null;
02538 crossfireUpdateMapListener.mapEnd();
02539 }
02540 }
02541 if (debugProtocol != null) {
02542 debugProtocol.debugProtocolWrite("recv map2 end");
02543 }
02544 notifyPacketWatcherListenersShortArray(packet, args);
02545 }
02546
02551 private void processMapExtended(@NotNull final ByteBuffer packet) {
02552 final int args = packet.position();
02553 if (debugProtocol != null) {
02554 debugProtocol.debugProtocolWrite("recv mapextended");
02555 }
02556
02557
02558
02559 notifyPacketWatcherListenersMixed(packet, args);
02560 }
02561
02566 private void processMusic(@NotNull final ByteBuffer packet) {
02567 final int args = packet.position();
02568 final String music = getString(packet, packet.remaining());
02569 if (debugProtocol != null) {
02570 debugProtocol.debugProtocolWrite("recv music "+music);
02571 }
02572
02573 fireMusicReceived(music);
02574 notifyPacketWatcherListenersAscii(packet, args);
02575 }
02576
02582 private void processNewMap(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02583 final int args = packet.position();
02584 if (packet.hasRemaining()) {
02585 throw new UnknownCommandException("excess data at end of newmap command");
02586 }
02587 if (debugProtocol != null) {
02588 debugProtocol.debugProtocolWrite("recv newmap");
02589 }
02590 fireNewMap();
02591 notifyPacketWatcherListenersNoData(packet, args);
02592 }
02593
02599 private void processPickup(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02600 final int args = packet.position();
02601 final int pickupOptions = getInt4(packet);
02602 if (packet.hasRemaining()) {
02603 throw new UnknownCommandException("excess data at end of pickup command");
02604 }
02605 if (debugProtocol != null) {
02606 debugProtocol.debugProtocolWrite("recv pickup options="+pickupOptions);
02607 }
02608 firePickupChanged(pickupOptions);
02609 notifyPacketWatcherListenersMixed(packet, args);
02610 }
02611
02617 private void processPlayer(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02618 final int args = packet.position();
02619 final int tag = getInt4(packet);
02620 final int weight = getInt4(packet);
02621 final int faceNum = getInt4(packet);
02622 final int nameLength = getInt1(packet);
02623 final String name = getString(packet, nameLength);
02624 if (packet.hasRemaining()) {
02625 throw new UnknownCommandException("excess data at end of player command");
02626 }
02627 if (debugProtocol != null) {
02628 debugProtocol.debugProtocolWrite("recv player tag="+tag+" weight="+weight+" face="+faceNum+" name="+name);
02629 }
02630 firePlayerReceived(tag, weight, faceNum, name);
02631 notifyPacketWatcherListenersMixed(packet, args);
02632 }
02633
02639 private void processQuery(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02640 final int args = packet.position();
02641 int flags = 0;
02642 do {
02643 flags = flags*10+parseDigit(packet.get());
02644 } while (packet.get(packet.position()) != ' ');
02645 packet.get();
02646
02647 final String text = getString(packet, packet.remaining());
02648
02649 if (debugProtocol != null) {
02650 debugProtocol.debugProtocolWrite("recv query flags="+flags+" text="+text);
02651 }
02652
02653
02654 if (clientSocketState != ClientSocketState.CONNECTED) {
02655 setClientSocketState(ClientSocketState.ADDME, ClientSocketState.CONNECTED);
02656 negotiateMapSize(preferredMapWidth, preferredMapHeight);
02657 negotiateNumLookObjects(preferredNumLookObjects);
02658 }
02659 fireCommandQueryReceived(text, flags);
02660 notifyPacketWatcherListenersAscii(packet, args);
02661 }
02662
02668 private void processReplyInfo(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02669 final int args = packet.position();
02670 final int startPos = packet.position();
02671 while (packet.hasRemaining() && packet.get(packet.position()) != '\n' && packet.get(packet.position()) != ' ') {
02672 packet.get();
02673 }
02674 final String infoType = newString(packet, startPos, packet.position()-startPos);
02675 if (packet.hasRemaining()) {
02676 packet.get();
02677 }
02678 if (debugProtocol != null) {
02679 debugProtocol.debugProtocolWrite("recv replyinfo type="+infoType+" len="+packet.remaining());
02680 }
02681 try {
02682 cmdReplyinfo(infoType, packet);
02683 } catch (final IOException ex) {
02684 throw new UnknownCommandException("invalid replyinfo command: "+ex.getMessage());
02685 }
02686 notifyPacketWatcherListenersAscii(packet, args);
02687 }
02688
02694 private void processSetup(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02695 final int args = packet.position();
02696 final List<String> options = new ArrayList<String>();
02697 while (packet.hasRemaining()) {
02698 while (packet.get(packet.position()) == ' ') {
02699 packet.get();
02700 }
02701 final int startPos = packet.position();
02702 while (packet.hasRemaining() && packet.get(packet.position()) != ' ') {
02703 packet.get();
02704 }
02705 options.add(newString(packet, startPos, packet.position()-startPos));
02706 if (packet.hasRemaining()) {
02707 packet.get();
02708 }
02709 }
02710 if (debugProtocol != null) {
02711 debugProtocol.debugProtocolWrite("recv setup "+options);
02712 }
02713 if (options.size()%2 != 0) {
02714 throw new UnknownCommandException("odd number of arguments in setup command");
02715 }
02716 for (int i = 0; i+1 < options.size(); i += 2) {
02717 final String option = options.get(i);
02718 final String value = options.get(i+1);
02719 if (option.equals("spellmon")) {
02720 if (!value.equals("1")) {
02721 throw new UnknownCommandException("Error: the server is too old for this client since it does not support the spellmon=1 setup option.");
02722 }
02723 } else if (option.equals("sound2")) {
02724
02725 } else if (option.equals("exp64")) {
02726
02727
02728 } else if (option.equals("newmapcmd")) {
02729 if (!value.equals("1")) {
02730 throw new UnknownCommandException("Error: the server is too old for this client since it does not support the newmapcmd=1 setup option.");
02731 }
02732 } else if (option.equals("facecache")) {
02733 if (!value.equals("1")) {
02734 throw new UnknownCommandException("the server is too old for this client since it does not support the facecache=1 setup option.");
02735 }
02736 } else if (option.equals("extendedTextInfos")) {
02737 if (!value.equals("1")) {
02738 throw new UnknownCommandException("the server is too old for this client since it does not support the extendedTextInfos=1 setup option.");
02739 }
02740 } else if (option.equals("itemcmd")) {
02741 if (!value.equals("2")) {
02742 throw new UnknownCommandException("the server is too old for this client since it does not support the itemcmd=2 setup option.");
02743 }
02744 } else if (option.equals("mapsize")) {
02745 final String[] tmp = value.split("x", 2);
02746 if (tmp.length != 2) {
02747 throw new UnknownCommandException("the server returned 'setup mapsize "+value+"'.");
02748 }
02749 final int thisMapWidth;
02750 final int thisMapHeight;
02751 try {
02752 thisMapWidth = Integer.parseInt(tmp[0]);
02753 thisMapHeight = Integer.parseInt(tmp[1]);
02754 } catch (final NumberFormatException ignored) {
02755 throw new UnknownCommandException("the server returned 'setup mapsize "+value+"'.");
02756 }
02757 if (pendingMapWidth == 0 || pendingMapHeight == 0) {
02758 System.err.println("the server sent an unexpected 'setup mapsize "+value+"'.");
02759 } else if (pendingMapWidth == thisMapWidth && pendingMapHeight == thisMapHeight) {
02760 pendingMapWidth = 0;
02761 pendingMapHeight = 0;
02762 setCurrentMapSize(thisMapWidth, thisMapHeight);
02763 if (thisMapWidth != preferredMapWidth && thisMapHeight != preferredMapHeight) {
02764 negotiateMapSize(preferredMapWidth, preferredMapHeight);
02765 }
02766 } else if (pendingMapWidth > thisMapWidth && pendingMapHeight > thisMapHeight) {
02767 pendingMapWidth = 0;
02768 pendingMapHeight = 0;
02769 negotiateMapSize(thisMapWidth, thisMapHeight);
02770 } else if (pendingMapWidth > thisMapWidth) {
02771 final int tmpMapHeight = pendingMapHeight;
02772 pendingMapWidth = 0;
02773 pendingMapHeight = 0;
02774 negotiateMapSize(thisMapWidth, tmpMapHeight);
02775 } else if (pendingMapHeight > thisMapHeight) {
02776 final int tmpMapWidth = pendingMapWidth;
02777 pendingMapWidth = 0;
02778 pendingMapHeight = 0;
02779 negotiateMapSize(tmpMapWidth, thisMapHeight);
02780 } else if (pendingMapWidth == thisMapWidth) {
02781 final int tmpMapHeight = pendingMapHeight+2;
02782 pendingMapWidth = 0;
02783 pendingMapHeight = 0;
02784 negotiateMapSize(thisMapWidth, tmpMapHeight);
02785 } else if (pendingMapHeight == thisMapHeight) {
02786 final int tmpMapWidth = pendingMapWidth+2;
02787 pendingMapWidth = 0;
02788 pendingMapHeight = 0;
02789 negotiateMapSize(tmpMapWidth, thisMapHeight);
02790 } else if (pendingMapWidth <= pendingMapHeight) {
02791 final int tmpMapWidth = pendingMapWidth+2;
02792 final int tmpMapHeight = pendingMapHeight;
02793 pendingMapWidth = 0;
02794 pendingMapHeight = 0;
02795 negotiateMapSize(tmpMapWidth, tmpMapHeight);
02796 } else {
02797 final int tmpMapWidth = pendingMapWidth;
02798 final int tmpMapHeight = pendingMapHeight+2;
02799 pendingMapWidth = 0;
02800 pendingMapHeight = 0;
02801 negotiateMapSize(tmpMapWidth, tmpMapHeight);
02802 }
02803 } else if (option.equals("map2cmd")) {
02804 if (!value.equals("1")) {
02805 throw new UnknownCommandException("the server is too old for this client since it does not support the map2cmd=1 setup option.");
02806 }
02807 } else if (option.equals("darkness")) {
02808
02809 } else if (option.equals("tick")) {
02810 if (!value.equals("1")) {
02811 throw new UnknownCommandException("the server is too old for this client since it does not support the tick=1 setup option.");
02812 }
02813 } else if (option.equals("num_look_objects")) {
02814 if (value.equals("FALSE")) {
02815 System.err.println("Warning: the server is too old for this client since it does not support the num_look_objects setup option.");
02816 System.err.println("Expect issues with the ground view display.");
02817 pendingNumLookObjects = 0;
02818 if (debugProtocol != null) {
02819 debugProtocol.debugProtocolWrite("processSetup: pending_num_look_objects="+pendingNumLookObjects+" [server didn't understand setup command]");
02820 }
02821 } else {
02822 final int thisNumLookObjects;
02823 try {
02824 thisNumLookObjects = Integer.parseInt(value);
02825 } catch (final NumberFormatException ignored) {
02826 throw new UnknownCommandException("the server returned 'setup num_look_objects "+value+"'.");
02827 }
02828 if (pendingNumLookObjects == 0) {
02829 System.err.println("the server sent an unexpected 'setup num_look_objects "+value+"'.");
02830 } else {
02831 if (pendingNumLookObjects != thisNumLookObjects) {
02832 System.err.println("Warning: the server didn't accept the num_look_objects setup option: requested "+pendingNumLookObjects+", returned "+thisNumLookObjects+".");
02833 System.err.println("Expect issues with the ground view display.");
02834 }
02835 pendingNumLookObjects = 0;
02836 if (debugProtocol != null) {
02837 debugProtocol.debugProtocolWrite("processSetup: pending_num_look_objects="+pendingNumLookObjects+" [ok]");
02838 }
02839 currentNumLookObjects = thisNumLookObjects;
02840 if (debugProtocol != null) {
02841 debugProtocol.debugProtocolWrite("processSetup: num_look_objects="+currentNumLookObjects);
02842 }
02843 if (currentNumLookObjects != preferredNumLookObjects) {
02844 negotiateNumLookObjects(preferredNumLookObjects);
02845 }
02846 }
02847 }
02848 } else if (option.equals("faceset")) {
02849
02850 } else if (option.equals("want_pickup")) {
02851
02852 } else if (option.equals("extended_stats")) {
02853
02854 } else if (option.equals("loginmethod")) {
02855 if (value.equals("FALSE")) {
02856 loginMethod = 0;
02857 continue;
02858 }
02859
02860 final int method;
02861 try {
02862 method = Integer.parseInt(value);
02863 } catch (final NumberFormatException ignored) {
02864 throw new UnknownCommandException("the server returned 'setup loginmethod "+value+"'.");
02865 }
02866 loginMethod = method;
02867 } else if (option.equals("notifications")) {
02868
02869 } else {
02870 System.err.println("Warning: ignoring unknown setup option from server: "+option+"="+value);
02871 }
02872 }
02873
02874 if (options.size() != 2 || !options.get(0).equals("mapsize") && !options.get(0).equals("num_look_objects")) {
02875 setClientSocketState(ClientSocketState.SETUP, ClientSocketState.REQUESTINFO);
02876 sendRequestinfo("image_info");
02877 sendRequestinfo("skill_info");
02878 sendRequestinfo("exp_table");
02879 sendToggleextendedtext(MessageTypes.getAllTypes());
02880 }
02881 notifyPacketWatcherListenersAscii(packet, args);
02882 }
02883
02889 private void processSmooth(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02890 final int args = packet.position();
02891 final int faceNo = getInt2(packet);
02892 final int smoothPic = getInt2(packet);
02893 if (packet.hasRemaining()) {
02894 throw new UnknownCommandException("excess data at end of smooth command");
02895 }
02896 if (debugProtocol != null) {
02897 debugProtocol.debugProtocolWrite("recv smooth face="+faceNo+" smooth_pic="+smoothPic);
02898 }
02899 model.getSmoothFaces().updateSmoothFace(faceNo, smoothPic);
02900 notifyPacketWatcherListenersShortArray(packet, args);
02901 }
02902
02908 private void processSound(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02909 final int args = packet.position();
02910 final int x = packet.get();
02911 final int y = packet.get();
02912 final int num = getInt2(packet);
02913 final int type = getInt1(packet);
02914 if (packet.hasRemaining()) {
02915 throw new UnknownCommandException("excess data at end of sound command");
02916 }
02917 if (debugProtocol != null) {
02918 debugProtocol.debugProtocolWrite("recv sound pos="+x+"/"+y+" num="+num+" type="+type);
02919 }
02920
02921 fireCommandSoundReceived(x, y, num, type);
02922 notifyPacketWatcherListenersMixed(packet, args);
02923 }
02924
02930 private void processSound2(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02931 final int args = packet.position();
02932 final int x = packet.get();
02933 final int y = packet.get();
02934 final int dir = packet.get();
02935 final int volume = getInt1(packet);
02936 final int type = getInt1(packet);
02937 final int actionLength = getInt1(packet);
02938 final String action = getString(packet, actionLength);
02939 final int nameLength = getInt1(packet);
02940 final String name = getString(packet, nameLength);
02941 if (packet.hasRemaining()) {
02942 throw new UnknownCommandException("excess data at end of sound2 command");
02943 }
02944 if (debugProtocol != null) {
02945 debugProtocol.debugProtocolWrite("recv sound2 pos="+x+"/"+y+" dir="+dir+" volume="+volume+" type="+type+" action="+action+" name="+name);
02946 }
02947
02948 fireCommandSound2Received(x, y, dir, volume, type, action, name);
02949 notifyPacketWatcherListenersMixed(packet, args);
02950 }
02951
02957 private void processStats(@NotNull final ByteBuffer packet) throws UnknownCommandException {
02958 while (packet.hasRemaining()) {
02959 final int stat = getInt1(packet);
02960 switch (stat) {
02961 case Stats.CS_STAT_HP:
02962 case Stats.CS_STAT_MAXHP:
02963 case Stats.CS_STAT_SP:
02964 case Stats.CS_STAT_MAXSP:
02965 case Stats.CS_STAT_STR:
02966 case Stats.CS_STAT_INT:
02967 case Stats.CS_STAT_WIS:
02968 case Stats.CS_STAT_DEX:
02969 case Stats.CS_STAT_CON:
02970 case Stats.CS_STAT_CHA:
02971 case Stats.CS_STAT_LEVEL:
02972 case Stats.CS_STAT_WC:
02973 case Stats.CS_STAT_AC:
02974 case Stats.CS_STAT_DAM:
02975 case Stats.CS_STAT_ARMOUR:
02976 case Stats.CS_STAT_FOOD:
02977 case Stats.CS_STAT_POW:
02978 case Stats.CS_STAT_GRACE:
02979 case Stats.CS_STAT_MAXGRACE:
02980 case Stats.CS_STAT_FLAGS:
02981 case Stats.CS_STAT_RACE_STR:
02982 case Stats.CS_STAT_RACE_INT:
02983 case Stats.CS_STAT_RACE_WIS:
02984 case Stats.CS_STAT_RACE_DEX:
02985 case Stats.CS_STAT_RACE_CON:
02986 case Stats.CS_STAT_RACE_CHA:
02987 case Stats.CS_STAT_RACE_POW:
02988 case Stats.CS_STAT_BASE_STR:
02989 case Stats.CS_STAT_BASE_INT:
02990 case Stats.CS_STAT_BASE_WIS:
02991 case Stats.CS_STAT_BASE_DEX:
02992 case Stats.CS_STAT_BASE_CON:
02993 case Stats.CS_STAT_BASE_CHA:
02994 case Stats.CS_STAT_BASE_POW:
02995 case Stats.CS_STAT_APPLIED_STR:
02996 case Stats.CS_STAT_APPLIED_INT:
02997 case Stats.CS_STAT_APPLIED_WIS:
02998 case Stats.CS_STAT_APPLIED_DEX:
02999 case Stats.CS_STAT_APPLIED_CON:
03000 case Stats.CS_STAT_APPLIED_CHA:
03001 case Stats.CS_STAT_APPLIED_POW:
03002 case Stats.CS_STAT_GOLEM_HP:
03003 case Stats.CS_STAT_GOLEM_MAXHP:
03004 final short int2Param = (short)getInt2(packet);
03005 if (debugProtocol != null) {
03006 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" int2="+int2Param+"="+(int2Param&0xFFFF));
03007 }
03008 model.getStats().setStatInt2(stat, int2Param);
03009 notifyPacketWatcherListenersStats(stat, int2Param);
03010 break;
03011
03012 case Stats.CS_STAT_EXP:
03013 case Stats.CS_STAT_SPEED:
03014 case Stats.CS_STAT_WEAP_SP:
03015 case Stats.CS_STAT_WEIGHT_LIM:
03016 case Stats.CS_STAT_SPELL_ATTUNE:
03017 case Stats.CS_STAT_SPELL_REPEL:
03018 case Stats.CS_STAT_SPELL_DENY:
03019 final int int4Param = getInt4(packet);
03020 if (debugProtocol != null) {
03021 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" int4="+int4Param);
03022 }
03023 model.getStats().setStatInt4(stat, int4Param);
03024 notifyPacketWatcherListenersStats(stat, int4Param);
03025 break;
03026
03027 case Stats.CS_STAT_EXP64:
03028 final long int8Param = getInt8(packet);
03029 if (debugProtocol != null) {
03030 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" int8="+int8Param);
03031 }
03032 model.getStats().setStatInt8(stat, int8Param);
03033 notifyPacketWatcherListenersStats(stat, int8Param);
03034 break;
03035
03036 case Stats.CS_STAT_RANGE:
03037 case Stats.CS_STAT_TITLE:
03038 final int length = getInt1(packet);
03039 final String strParam = getString(packet, length);
03040 if (debugProtocol != null) {
03041 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" str="+strParam);
03042 }
03043 model.getStats().setStatString(stat, strParam);
03044 notifyPacketWatcherListenersStats(stat, strParam);
03045 break;
03046
03047 default:
03048 if (Stats.CS_STAT_RESIST_START <= stat && stat < Stats.CS_STAT_RESIST_START+Stats.RESIST_TYPES) {
03049 final short int2Param2 = (short)getInt2(packet);
03050 if (debugProtocol != null) {
03051 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" int2="+int2Param2);
03052 }
03053 model.getStats().setStatInt2(stat, int2Param2);
03054 notifyPacketWatcherListenersStats(stat, int2Param2);
03055 } else if (Stats.CS_STAT_SKILLINFO <= stat && stat < Stats.CS_STAT_SKILLINFO+Stats.CS_NUM_SKILLS) {
03056 final int level = getInt1(packet);
03057 final long experience = getInt8(packet);
03058 if (debugProtocol != null) {
03059 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" level="+level+" experience="+experience);
03060 }
03061 model.getStats().setStatSkill(stat, level, experience);
03062 notifyPacketWatcherListenersStats(stat, level, experience);
03063 } else {
03064 if (debugProtocol != null) {
03065 debugProtocol.debugProtocolWrite("recv stats stat="+stat+" <unknown parameter>");
03066 }
03067 throw new UnknownCommandException("unknown stat value: "+stat);
03068 }
03069 break;
03070 }
03071 }
03072 }
03073
03079 private void processTick(@NotNull final ByteBuffer packet) throws UnknownCommandException {
03080 final int args = packet.position();
03081 final int tickNo = getInt4(packet);
03082 if (packet.hasRemaining()) {
03083 throw new UnknownCommandException("excess data at end of tick command");
03084 }
03085 if (debugProtocol != null) {
03086 debugProtocol.debugProtocolWrite("recv tick "+tickNo);
03087 }
03088 fireTick(tickNo);
03089 notifyPacketWatcherListenersIntArray(packet, args);
03090 }
03091
03097 private void processUpdItem(@NotNull final ByteBuffer packet) throws UnknownCommandException {
03098 final int args = packet.position();
03099 final int flags = getInt1(packet);
03100 final int tag = getInt4(packet);
03101 final int valLocation = (flags&UpdItem.UPD_LOCATION) == 0 ? 0 : getInt4(packet);
03102 final int valFlags = (flags&UpdItem.UPD_FLAGS) == 0 ? 0 : getInt4(packet);
03103 final int valWeight = (flags&UpdItem.UPD_WEIGHT) == 0 ? 0 : getInt4(packet);
03104 final int valFaceNum = (flags&UpdItem.UPD_FACE) == 0 ? 0 : getInt4(packet);
03105 final String valName;
03106 final String valNamePl;
03107 if ((flags&UpdItem.UPD_NAME) == 0) {
03108 valName = "";
03109 valNamePl = "";
03110 } else {
03111 final int nameLength = getInt1(packet);
03112 int namePlIndex = 0;
03113 while (namePlIndex < nameLength && packet.get(packet.position()+namePlIndex) != 0) {
03114 namePlIndex++;
03115 }
03116 valName = newString(packet, packet.position(), namePlIndex);
03117 valNamePl = namePlIndex+1 < nameLength ? newString(packet, packet.position()+namePlIndex+1, nameLength-(namePlIndex+1)) : valName;
03118 packet.position(packet.position()+nameLength);
03119 }
03120 final int valAnim = (flags&UpdItem.UPD_ANIM) == 0 ? 0 : getInt2(packet);
03121 final int valAnimSpeed = (flags&UpdItem.UPD_ANIMSPEED) == 0 ? 0 : getInt1(packet);
03122 final int valNrof = (flags&UpdItem.UPD_NROF) == 0 ? 0 : getInt4(packet);
03123 if (packet.hasRemaining()) {
03124 throw new UnknownCommandException("excess data at end of upditem command");
03125 }
03126 if (debugProtocol != null) {
03127 debugProtocol.debugProtocolWrite("recv upditem flags="+flags+" tag="+tag+" loc="+valLocation+" flags="+valFlags+" weight="+valWeight+" face="+valFaceNum+" name="+valName+" name_pl="+valNamePl+" anim="+valAnim+" anim_speed="+valAnimSpeed+" nrof="+valNrof);
03128 }
03129 fireUpditemReceived(flags, tag, valLocation, valFlags, valWeight, valFaceNum, valName, valNamePl, valAnim, valAnimSpeed, valNrof);
03130 notifyPacketWatcherListenersMixed(packet, args);
03131 }
03132
03137 private void processUpdQuest(@NotNull final ByteBuffer packet) {
03138 final int args = packet.position();
03139 final int code = getInt4(packet);
03140 final int end = getInt1(packet);
03141 final int stepLength = getInt2(packet);
03142 final String step = stepLength > 0 ? getString(packet, stepLength) : "";
03143
03144 if (debugProtocol != null) {
03145 debugProtocol.debugProtocolWrite("recv updquest code="+code+" end="+end+" description="+step);
03146 }
03147 model.getQuestsManager().updateQuest(code, end == 1, step);
03148 notifyPacketWatcherListenersMixed(packet, args);
03149 }
03150
03156 private void processUpdSpell(@NotNull final ByteBuffer packet) throws UnknownCommandException {
03157 final int args = packet.position();
03158 final int flags = getInt1(packet);
03159 final int tag = getInt4(packet);
03160 final int mana = (flags&SpellsManager.UPD_SP_MANA) == 0 ? 0 : getInt2(packet);
03161 final int grace = (flags&SpellsManager.UPD_SP_GRACE) == 0 ? 0 : getInt2(packet);
03162 final int damage = (flags&SpellsManager.UPD_SP_DAMAGE) == 0 ? 0 : getInt2(packet);
03163 if (packet.hasRemaining()) {
03164 throw new UnknownCommandException("excess data at end of updspell command");
03165 }
03166 if (debugProtocol != null) {
03167 debugProtocol.debugProtocolWrite("recv updspell flags="+flags+" tag="+tag+" sp="+mana+" gr="+grace+" dam="+damage);
03168 }
03169 model.getSpellsManager().updateSpell(flags, tag, mana, grace, damage);
03170 notifyPacketWatcherListenersMixed(packet, args);
03171 }
03172
03178 private void processVersion(@NotNull final ByteBuffer packet) throws UnknownCommandException {
03179 final int args = packet.position();
03180 int csval = 0;
03181 do {
03182 csval = csval*10+parseDigit(packet.get());
03183 } while (packet.get(packet.position()) != ' ');
03184 packet.get();
03185
03186 int scval = 0;
03187 do {
03188 scval = scval*10+parseDigit(packet.get());
03189 } while (packet.get(packet.position()) != ' ');
03190 packet.get();
03191
03192 final String vinfo = getString(packet, packet.remaining());
03193
03194 if (debugProtocol != null) {
03195 debugProtocol.debugProtocolWrite("recv version cs="+csval+" sc="+scval+" info="+vinfo);
03196 }
03197
03198 setClientSocketState(ClientSocketState.VERSION, ClientSocketState.SETUP);
03199 sendSetup("want_pickup 1", "faceset 0", "sound2 3", "exp64 1", "map2cmd 1", "darkness 1", "newmapcmd 1", "facecache 1", "extendedTextInfos 1", "itemcmd 2", "spellmon 1", "tick 1", "extended_stats 1", "loginmethod 1", "notifications 1");
03200 model.getStats().setSimpleWeaponSpeed(scval >= 1029);
03201
03202 notifyPacketWatcherListenersAscii(packet, args);
03203 }
03204
03208 @Override
03209 public void sendAccountLogin(@NotNull final String login, @NotNull final String password) {
03210 clearFailure();
03211 if (debugProtocol != null) {
03212 debugProtocol.debugProtocolWrite("send accountlogin "+login);
03213 }
03214 accountName = login;
03215 synchronized (writeBuffer) {
03216 byteBuffer.clear();
03217 byteBuffer.put(ACCOUNT_LOGIN_PREFIX);
03218 final byte[] loginBytes = login.getBytes(UTF8);
03219 byteBuffer.put((byte)loginBytes.length);
03220 byteBuffer.put(loginBytes);
03221 final byte[] passwordBytes = password.getBytes(UTF8);
03222 byteBuffer.put((byte)passwordBytes.length);
03223 byteBuffer.put(passwordBytes);
03224 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03225 }
03226
03227 }
03228
03232 @Override
03233 public void sendAddme() {
03234 if (debugProtocol != null) {
03235 debugProtocol.debugProtocolWrite("send addme");
03236 }
03237 defaultServerConnection.writePacket(ADDME_PREFIX, ADDME_PREFIX.length);
03238 }
03239
03243 @Override
03244 public void sendApply(final int tag) {
03245 if (debugProtocol != null) {
03246 debugProtocol.debugProtocolWrite("send apply tag="+tag);
03247 }
03248 synchronized (writeBuffer) {
03249 byteBuffer.clear();
03250 byteBuffer.put(APPLY_PREFIX);
03251 putDecimal(tag);
03252 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03253 }
03254 }
03255
03259 @Override
03260 public void sendAskface(final int num) {
03261 if (debugProtocol != null) {
03262 debugProtocol.debugProtocolWrite("send askface face="+num);
03263 }
03264 synchronized (writeBuffer) {
03265 byteBuffer.clear();
03266 byteBuffer.put(ASKFACE_PREFIX);
03267 putDecimal(num);
03268 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03269 }
03270 }
03271
03275 @Override
03276 public void sendExamine(final int tag) {
03277 if (debugProtocol != null) {
03278 debugProtocol.debugProtocolWrite("send examine tag="+tag);
03279 }
03280 synchronized (writeBuffer) {
03281 byteBuffer.clear();
03282 byteBuffer.put(EXAMINE_PREFIX);
03283 putDecimal(tag);
03284 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03285 }
03286 }
03287
03291 @Override
03292 public void sendLock(final boolean val, final int tag) {
03293 if (debugProtocol != null) {
03294 debugProtocol.debugProtocolWrite("send lock tag="+tag+" val="+val);
03295 }
03296 synchronized (writeBuffer) {
03297 byteBuffer.clear();
03298 byteBuffer.put(LOCK_PREFIX);
03299 byteBuffer.put((byte)(val ? 1 : 0));
03300 byteBuffer.putInt(tag);
03301 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03302 }
03303 }
03304
03308 @Override
03309 public void sendLookat(final int dx, final int dy) {
03310 if (debugProtocol != null) {
03311 debugProtocol.debugProtocolWrite("send lookat pos="+dx+"/"+dy);
03312 }
03313 synchronized (writeBuffer) {
03314 byteBuffer.clear();
03315 byteBuffer.put(LOOKAT_PREFIX);
03316 putDecimal(dx);
03317 byteBuffer.put((byte)' ');
03318 putDecimal(dy);
03319 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03320 }
03321 }
03322
03326 @Override
03327 public void sendMark(final int tag) {
03328 if (debugProtocol != null) {
03329 debugProtocol.debugProtocolWrite("send mark tag="+tag);
03330 }
03331 synchronized (writeBuffer) {
03332 byteBuffer.clear();
03333 byteBuffer.put(MARK_PREFIX);
03334 byteBuffer.putInt(tag);
03335 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03336 }
03337 }
03338
03342 @Override
03343 public void sendMove(final int to, final int tag, final int nrof) {
03344 if (debugProtocol != null) {
03345 debugProtocol.debugProtocolWrite("send move tag="+tag+" to="+to+" nrof="+nrof);
03346 }
03347 synchronized (writeBuffer) {
03348 byteBuffer.clear();
03349 byteBuffer.put(MOVE_PREFIX);
03350 putDecimal(to);
03351 byteBuffer.put((byte)' ');
03352 putDecimal(tag);
03353 byteBuffer.put((byte)' ');
03354 putDecimal(nrof);
03355 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03356 }
03357 }
03358
03362 @Override
03363 public int sendNcom(final int repeat, @NotNull final String command) {
03364 if (debugProtocol != null) {
03365 debugProtocol.debugProtocolWrite("send ncom no="+packet+" repeat="+repeat+" cmd="+command);
03366 }
03367 final int thisPacket;
03368 synchronized (writeBuffer) {
03369 thisPacket = packet++&0x00FF;
03370 byteBuffer.clear();
03371 byteBuffer.put(NCOM_PREFIX);
03372 byteBuffer.putShort((short)thisPacket);
03373 byteBuffer.putInt(repeat);
03374 byteBuffer.put(command.getBytes(UTF8));
03375 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03376 }
03377 return thisPacket;
03378 }
03379
03383 @Override
03384 public void sendReply(@NotNull final String text) {
03385 if (debugProtocol != null) {
03386 debugProtocol.debugProtocolWrite("send reply text="+text);
03387 }
03388 synchronized (writeBuffer) {
03389 byteBuffer.clear();
03390 byteBuffer.put(REPLY_PREFIX);
03391 byteBuffer.put(text.getBytes(UTF8));
03392 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03393 }
03394 fireReplySent();
03395 }
03396
03400 @Override
03401 public void sendRequestinfo(@NotNull final String infoType) {
03402 if (debugProtocol != null) {
03403 debugProtocol.debugProtocolWrite("send requestinfo type="+infoType);
03404 }
03405 synchronized (writeBuffer) {
03406 byteBuffer.clear();
03407 byteBuffer.put(REQUESTINFO_PREFIX);
03408 byteBuffer.put(infoType.getBytes(UTF8));
03409 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03410 }
03411 }
03412
03416 @Override
03417 public void sendSetup(@NotNull final String... options) {
03418 if (debugProtocol != null) {
03419 debugProtocol.debugProtocolWrite("send setup options="+Arrays.toString(options));
03420 }
03421 synchronized (writeBuffer) {
03422 byteBuffer.clear();
03423 byteBuffer.put(SETUP_PREFIX);
03424 if (options.length <= 0) {
03425 byteBuffer.put((byte)' ');
03426 } else {
03427 for (final String option : options) {
03428 byteBuffer.put((byte)' ');
03429 byteBuffer.put(option.getBytes(UTF8));
03430 }
03431 }
03432 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03433 }
03434 }
03435
03439 @Override
03440 public void sendToggleextendedtext(@NotNull final int... types) {
03441 if (types.length <= 0) {
03442 return;
03443 }
03444
03445 if (debugProtocol != null) {
03446 debugProtocol.debugProtocolWrite("send toggleextendedtext types="+Arrays.toString(types));
03447 }
03448 synchronized (writeBuffer) {
03449 byteBuffer.clear();
03450 byteBuffer.put(TOGGLEEXTENDEDTEXT_PREFIX);
03451 for (final int type : types) {
03452 byteBuffer.put((byte)' ');
03453 putDecimal(type);
03454 }
03455 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03456 }
03457 }
03458
03462 @Override
03463 public void sendVersion(final int csval, final int scval, @NotNull final String vinfo) {
03464 if (debugProtocol != null) {
03465 debugProtocol.debugProtocolWrite("send version cs="+csval+" sc="+scval+" info="+vinfo);
03466 }
03467 synchronized (writeBuffer) {
03468 byteBuffer.clear();
03469 byteBuffer.put(VERSION_PREFIX);
03470 putDecimal(csval);
03471 byteBuffer.put((byte)' ');
03472 putDecimal(scval);
03473 byteBuffer.put((byte)' ');
03474 byteBuffer.put(vinfo.getBytes(UTF8));
03475 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03476 }
03477 }
03478
03484 private void putDecimal(final int value) {
03485 if (value == 0) {
03486 byteBuffer.put((byte)'0');
03487 } else {
03488 final String str = Integer.toString(value);
03489 try {
03490 byteBuffer.put(str.getBytes("ISO-8859-1"));
03491 } catch (final UnsupportedEncodingException ex) {
03492 throw new AssertionError(ex);
03493 }
03494 }
03495 }
03496
03503 private static int parseDigit(final byte ch) throws UnknownCommandException {
03504 final int digit = ch-'0';
03505 if (digit < 0 || digit > 9) {
03506 throw new UnknownCommandException("not a digit: "+ch);
03507 }
03508 return digit;
03509 }
03510
03514 @Override
03515 public void setPreferredMapSize(final int preferredMapWidth, final int preferredMapHeight) {
03516 final int preferredMapWidth2 = Math.max(1, preferredMapWidth|1);
03517 final int preferredMapHeight2 = Math.max(1, preferredMapHeight|1);
03518 if (this.preferredMapWidth == preferredMapWidth2 && this.preferredMapHeight == preferredMapHeight2) {
03519 return;
03520 }
03521
03522 this.preferredMapWidth = preferredMapWidth2;
03523 this.preferredMapHeight = preferredMapHeight2;
03524
03525 negotiateMapSize(this.preferredMapWidth, this.preferredMapHeight);
03526 }
03527
03533 private void setCurrentMapSize(final int currentMapWidth, final int currentMapHeight) {
03534 if (this.currentMapWidth == currentMapWidth && this.currentMapHeight == currentMapHeight) {
03535 return;
03536 }
03537
03538 this.currentMapWidth = currentMapWidth;
03539 this.currentMapHeight = currentMapHeight;
03540 fireNewMap();
03541 }
03542
03546 private void fireNewMap() {
03547 if (crossfireUpdateMapListener != null) {
03548 crossfireUpdateMapListener.newMap(currentMapWidth, currentMapHeight);
03549 }
03550 }
03551
03555 @Override
03556 public void setPreferredNumLookObjects(final int preferredNumLookObjects) {
03557 final int preferredNumLookObjects2 = Math.max(3, preferredNumLookObjects);
03558 if (this.preferredNumLookObjects == preferredNumLookObjects2) {
03559 return;
03560 }
03561
03562 this.preferredNumLookObjects = preferredNumLookObjects2;
03563
03564 negotiateNumLookObjects(this.preferredNumLookObjects);
03565 }
03566
03570 @Nullable
03571 @Override
03572 public String getAccountName() {
03573 return accountName;
03574 }
03575
03579 @Override
03580 public void connect(@NotNull final String hostname, final int port) {
03581 accountName = null;
03582 clearFailure();
03583 clientSocketState = ClientSocketState.CONNECTING;
03584 setClientSocketState(ClientSocketState.CONNECTING, ClientSocketState.CONNECTING);
03585 defaultServerConnection.connect(hostname, port);
03586 }
03587
03591 @Override
03592 public void disconnect(@NotNull final String reason) {
03593 defaultServerConnection.disconnect(reason);
03594 }
03595
03599 @Override
03600 public void addClientSocketListener(@NotNull final ClientSocketListener clientSocketListener) {
03601 defaultServerConnection.addClientSocketListener(clientSocketListener);
03602 }
03603
03607 @Override
03608 public void removeClientSocketListener(@NotNull final ClientSocketListener clientSocketListener) {
03609 defaultServerConnection.removeClientSocketListener(clientSocketListener);
03610 }
03611
03617 private void setClientSocketState(@NotNull final ClientSocketState prevState, @NotNull final ClientSocketState nextState) {
03618 if (debugProtocol != null) {
03619 debugProtocol.debugProtocolWrite("connection state: "+nextState);
03620 }
03621 if (clientSocketState != prevState) {
03622 System.err.println("Warning: connection state is "+clientSocketState+" when switching to state "+nextState+", expecting state "+prevState);
03623 }
03624 clientSocketState = nextState;
03625 model.getGuiStateManager().setClientSocketState(nextState);
03626 }
03627
03631 @Override
03632 public void sendAccountPlay(@NotNull final String name) {
03633 clearFailure();
03634 if (debugProtocol != null) {
03635 debugProtocol.debugProtocolWrite("send accountplay "+name);
03636 }
03637 synchronized (writeBuffer) {
03638 byteBuffer.clear();
03639 byteBuffer.put(ACCOUNT_PLAY_PREFIX);
03640 byteBuffer.put(name.getBytes(UTF8));
03641 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03642 }
03643
03644 final String tmpAccountName = accountName;
03645 if (tmpAccountName != null) {
03646 fireSelectCharacter(tmpAccountName, name);
03647 }
03648 }
03649
03653 @Override
03654 public void sendAccountLink(final int force, @NotNull final String login, @NotNull final String password) {
03655 clearFailure();
03656 if (debugProtocol != null) {
03657 debugProtocol.debugProtocolWrite("send accountaddplayer "+login);
03658 }
03659 synchronized (writeBuffer) {
03660 byteBuffer.clear();
03661 byteBuffer.put(ACCOUNT_ADD_PLAYER_PREFIX);
03662 byteBuffer.put((byte)force);
03663 final byte[] loginBytes = login.getBytes(UTF8);
03664 byteBuffer.put((byte)loginBytes.length);
03665 byteBuffer.put(loginBytes);
03666 final byte[] passwordBytes = password.getBytes(UTF8);
03667 byteBuffer.put((byte)passwordBytes.length);
03668 byteBuffer.put(passwordBytes);
03669 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03670 }
03671 }
03672
03676 @Override
03677 public void sendAccountCreate(@NotNull final String login, @NotNull final String password) {
03678 clearFailure();
03679 if (debugProtocol != null) {
03680 debugProtocol.debugProtocolWrite("send accountnew "+login);
03681 }
03682 accountName = login;
03683 synchronized (writeBuffer) {
03684 byteBuffer.clear();
03685 byteBuffer.put(ACCOUNT_NEW_PREFIX);
03686 final byte[] loginBytes = login.getBytes(UTF8);
03687 byteBuffer.put((byte)loginBytes.length);
03688 byteBuffer.put(loginBytes);
03689 final byte[] passwordBytes = password.getBytes(UTF8);
03690 byteBuffer.put((byte)passwordBytes.length);
03691 byteBuffer.put(passwordBytes);
03692 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03693 }
03694 }
03695
03699 @Override
03700 public void sendAccountCharacterCreate(@NotNull final String login, @NotNull final String password) {
03701 clearFailure();
03702 if (debugProtocol != null) {
03703 debugProtocol.debugProtocolWrite("send createplayer "+login);
03704 }
03705 synchronized (writeBuffer) {
03706 byteBuffer.clear();
03707 byteBuffer.put(CREATE_PLAYER_PREFIX);
03708 final byte[] loginBytes = login.getBytes(UTF8);
03709 byteBuffer.put((byte)loginBytes.length);
03710 byteBuffer.put(loginBytes);
03711 final byte[] passwordBytes = password.getBytes(UTF8);
03712 byteBuffer.put((byte)passwordBytes.length);
03713 byteBuffer.put(passwordBytes);
03714 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03715 }
03716 }
03717
03721 @Override
03722 public void sendAccountPassword(@NotNull final String currentPassword, @NotNull final String newPassword) {
03723 clearFailure();
03724 if (debugProtocol != null) {
03725 debugProtocol.debugProtocolWrite("send accountpw");
03726 }
03727 synchronized (writeBuffer) {
03728 byteBuffer.clear();
03729 byteBuffer.put(ACCOUNT_PASSWORD_PREFIX);
03730 final byte[] currentPasswordBytes = currentPassword.getBytes(UTF8);
03731 byteBuffer.put((byte)currentPasswordBytes.length);
03732 byteBuffer.put(currentPasswordBytes);
03733 final byte[] newPasswordBytes = newPassword.getBytes(UTF8);
03734 byteBuffer.put((byte)newPasswordBytes.length);
03735 byteBuffer.put(newPasswordBytes);
03736 defaultServerConnection.writePacket(writeBuffer, byteBuffer.position());
03737 }
03738 }
03739
03746 private static int getInt1(@NotNull final ByteBuffer byteBuffer) {
03747 return byteBuffer.get()&0xFF;
03748 }
03749
03756 private static int getInt2(@NotNull final ByteBuffer byteBuffer) {
03757 return byteBuffer.getShort()&0xFFFF;
03758 }
03759
03766 private static int getInt4(@NotNull final ByteBuffer byteBuffer) {
03767 return byteBuffer.getInt();
03768 }
03769
03776 private static long getInt8(@NotNull final ByteBuffer byteBuffer) {
03777 return byteBuffer.getLong();
03778 }
03779
03787 private static String getString(final ByteBuffer byteBuffer, final int len) {
03788 final byte[] tmp = new byte[len];
03789 byteBuffer.get(tmp);
03790 return new String(tmp, UTF8);
03791 }
03792
03798 @NotNull
03799 private static String hexDump(@NotNull final ByteBuffer byteBuffer) {
03800 final int len = byteBuffer.limit();
03801 final byte[] data = new byte[len];
03802 for (int i = 0; i < len; i++) {
03803 data[i] = byteBuffer.get(i);
03804 }
03805 return HexCodec.hexDump(data, 0, len);
03806 }
03807
03812 public int getCurrentNumLookObjects() {
03813 return currentNumLookObjects;
03814 }
03815
03816 }