├── .gitignore
├── src
├── test
│ ├── resources
│ │ └── com
│ │ │ └── github
│ │ │ └── koraktor
│ │ │ └── steamcondenser
│ │ │ ├── community
│ │ │ ├── invalid.xml
│ │ │ └── koraktor-friends.json
│ │ │ └── servers
│ │ │ ├── status_goldsrc
│ │ │ └── status_source
│ └── java
│ │ └── com
│ │ └── github
│ │ └── koraktor
│ │ └── steamcondenser
│ │ ├── exceptions
│ │ └── WebApiExceptionTest.java
│ │ ├── community
│ │ ├── portal2
│ │ │ └── Portal2StatsTest.java
│ │ ├── GameStatsTestCase.java
│ │ └── l4d
│ │ │ └── L4D2StatsTest.java
│ │ └── servers
│ │ ├── ServerTest.java
│ │ ├── sockets
│ │ ├── MasterServerSocketTest.java
│ │ ├── QuerySocketTest.java
│ │ ├── RCONSocketTest.java
│ │ ├── SteamSocketTest.java
│ │ └── SourceSocketTest.java
│ │ └── GoldSrcServerTest.java
└── main
│ └── java
│ └── com
│ └── github
│ └── koraktor
│ └── steamcondenser
│ ├── exceptions
│ ├── ConnectionResetException.java
│ ├── PacketFormatException.java
│ ├── RCONNoAuthException.java
│ ├── RCONBanException.java
│ └── SteamCondenserException.java
│ ├── servers
│ ├── packets
│ │ ├── A2S_INFO_Packet.java
│ │ ├── A2S_SERVERQUERY_GETCHALLENGE_Packet.java
│ │ ├── rcon
│ │ │ ├── RCONAuthRequestPacket.java
│ │ │ ├── RCONExecRequestPacket.java
│ │ │ ├── RCONAuthResponse.java
│ │ │ ├── RCONTerminator.java
│ │ │ ├── RCONGoldSrcResponsePacket.java
│ │ │ ├── RCONExecResponsePacket.java
│ │ │ ├── RCONGoldSrcRequestPacket.java
│ │ │ ├── RCONPacketFactory.java
│ │ │ └── RCONPacket.java
│ │ ├── S2A_INFO_BasePacket.java
│ │ ├── A2S_RULES_Packet.java
│ │ ├── S2C_CHALLENGE_Packet.java
│ │ ├── A2S_PLAYER_Packet.java
│ │ ├── S2A_RULES_Packet.java
│ │ ├── S2A_PLAYER_Packet.java
│ │ ├── M2A_SERVER_BATCH_Packet.java
│ │ ├── SteamPacket.java
│ │ ├── S2A_INFO_DETAILED_Packet.java
│ │ ├── A2M_GET_SERVERS_BATCH2_Packet.java
│ │ └── S2A_INFO2_Packet.java
│ └── sockets
│ │ ├── MasterServerSocket.java
│ │ ├── QuerySocket.java
│ │ ├── SourceSocket.java
│ │ └── RCONSocket.java
│ ├── community
│ ├── l4d
│ │ ├── L4DWeapon.java
│ │ ├── L4DExplosive.java
│ │ ├── L4D2Weapon.java
│ │ ├── AbtractL4DWeapon.java
│ │ ├── L4DMap.java
│ │ ├── L4DStats.java
│ │ └── L4D2Map.java
│ ├── GameClass.java
│ ├── tf2
│ │ ├── TF2Sniper.java
│ │ ├── TF2Medic.java
│ │ ├── TF2ClassFactory.java
│ │ ├── TF2Spy.java
│ │ ├── TF2Engineer.java
│ │ ├── TF2Item.java
│ │ ├── TF2GoldenWrench.java
│ │ ├── TF2Stats.java
│ │ ├── TF2Class.java
│ │ ├── TF2Inventory.java
│ │ └── TF2BetaInventory.java
│ ├── dota2
│ │ ├── Dota2Item.java
│ │ ├── Dota2Inventory.java
│ │ └── Dota2BetaInventory.java
│ ├── GameWeapon.java
│ ├── portal2
│ │ ├── Portal2Stats.java
│ │ ├── Portal2Item.java
│ │ └── Portal2Inventory.java
│ ├── GameLeaderboardEntry.java
│ ├── alien_swarm
│ │ └── AlienSwarmWeapon.java
│ ├── dods
│ │ ├── DoDSWeapon.java
│ │ ├── DoDSStats.java
│ │ └── DoDSClass.java
│ └── css
│ │ ├── CSSMap.java
│ │ └── CSSWeapon.java
│ ├── Helper.java
│ └── PacketBuffer.java
├── .travis.yml
├── LICENSE
├── CONTRIBUTING.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | doc
2 | target
3 |
--------------------------------------------------------------------------------
/src/test/resources/com/github/koraktor/steamcondenser/community/invalid.xml:
--------------------------------------------------------------------------------
1 |
2 | Invalid
3 |
--------------------------------------------------------------------------------
/src/test/resources/com/github/koraktor/steamcondenser/servers/status_goldsrc:
--------------------------------------------------------------------------------
1 | # name userid uniqueid frag time ping loss adr
2 | # 1 "someone" 1 STEAM_0:0:123456 10 3:52 12 0 0
3 | # 2 "somebody" 2 STEAM_0:0:123457 3 2:42 34 0 0
4 |
--------------------------------------------------------------------------------
/src/test/resources/com/github/koraktor/steamcondenser/servers/status_source:
--------------------------------------------------------------------------------
1 | # userid name uniqueid score connected ping loss state
2 | # 1 "someone" STEAM_0:0:123456 10 3:52 12 0 active
3 | # 2 "somebody" STEAM_0:0:123457 3 2:42 34 0 active
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | os: linux
3 | dist: trusty
4 |
5 | jdk:
6 | - openjdk8
7 | - oraclejdk8
8 | - openjdk11
9 | - openjdk14
10 | - openjdk-ea
11 |
12 | jobs:
13 | allow_failures:
14 | - jdk: openjdk-ea
15 | fast_finish: true
16 |
17 | notifications:
18 | webhooks:
19 | urls:
20 | - https://webhooks.gitter.im/e/8229d26a4f90bcc6cd67
21 | script: mvn test -Dsurefire.useFile=false
22 |
23 | cache:
24 | directories:
25 | - $HOME/.m2
26 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/exceptions/ConnectionResetException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.exceptions;
9 |
10 | /**
11 | * Indicates that a connection has been reset by the peer
12 | *
13 | * @author Sebastian Staudt
14 | */
15 | public class ConnectionResetException extends SteamCondenserException {
16 |
17 | public ConnectionResetException() {
18 | super("Connection reset by peer");
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/A2S_INFO_Packet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | /**
11 | * The A2S_INFO_Packet class represents a A2S_INFO request send to the server
12 | *
13 | * @author Sebastian Staudt
14 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateServerInfo
15 | */
16 | public class A2S_INFO_Packet extends SteamPacket {
17 |
18 | /**
19 | * Creates a new A2S_INFO request object
20 | */
21 | public A2S_INFO_Packet() {
22 | super(SteamPacket.A2S_INFO_HEADER, "Source Engine Query\0".getBytes());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/exceptions/PacketFormatException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.exceptions;
9 |
10 | /**
11 | * This exception class indicates a problem when parsing packet data from the
12 | * responses received from a game or master server
13 | *
14 | * @author Sebastian Staudt
15 | */
16 | public class PacketFormatException extends SteamCondenserException {
17 |
18 | /**
19 | * Creates a new PacketFormatException instance
20 | *
21 | * @param message The message to attach to the exception
22 | */
23 | public PacketFormatException(String message) {
24 | super(message);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/exceptions/RCONNoAuthException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.exceptions;
9 |
10 | /**
11 | * This exception class indicates that you have not authenticated yet with the
12 | * game server you're trying to send commands via RCON
13 | *
14 | * @author Sebastian Staudt
15 | * @see com.github.koraktor.steamcondenser.servers.GameServer#rconAuth
16 | * @see com.github.koraktor.steamcondenser.servers.GameServer#rconExec
17 | */
18 | public class RCONNoAuthException extends SteamCondenserException {
19 |
20 | /**
21 | * Creates a new RCONNoAuthException instance
22 | */
23 | public RCONNoAuthException() {
24 | super("Not authenticated yet.");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/exceptions/RCONBanException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.exceptions;
9 |
10 | /**
11 | * This exception class indicates that the IP address your accessing the game
12 | * server from has been banned by the server
13 | *
14 | * You or the server operator will have to unban your IP address on the server.
15 | *
16 | * @author Sebastian Staudt
17 | * @see com.github.koraktor.steamcondenser.servers.GameServer#rconAuth
18 | */
19 | public class RCONBanException extends SteamCondenserException {
20 |
21 | /**
22 | * Creates a new RCONBanException instance
23 | */
24 | public RCONBanException() {
25 | super("You have been banned from this server.");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/L4DWeapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * This class represents the statistics of a single weapon for a user in
14 | * Left4Dead
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class L4DWeapon extends AbtractL4DWeapon {
19 |
20 | /**
21 | * Creates a new instance of a weapon based on the given XML data
22 | *
23 | * @param weaponData The XML data for this weapon
24 | */
25 | public L4DWeapon(XMLData weaponData) {
26 | super(weaponData);
27 |
28 | this.killPercentage = Float.parseFloat(weaponData
29 | .getString("killpct").replace("%", "")) * 0.01f;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/A2S_SERVERQUERY_GETCHALLENGE_Packet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | /**
11 | * This packet class represents a A2S_SERVERQUERY_GETCHALLENGE request send to
12 | * a game server
13 | *
14 | * It is used to retrieve a challenge number from the game server, which helps
15 | * to identify the requesting client.
16 | *
17 | * @author Sebastian Staudt
18 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateChallengeNumber
19 | */
20 | public class A2S_SERVERQUERY_GETCHALLENGE_Packet extends SteamPacket {
21 |
22 | /**
23 | * Creates a new A2S_SERVERQUERY_GETCHALLENGE request object
24 | */
25 | public A2S_SERVERQUERY_GETCHALLENGE_Packet() {
26 | super(SteamPacket.A2S_SERVERQUERY_GETCHALLENGE_HEADER);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/GameClass.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community;
9 |
10 | /**
11 | * An abstract class implementing basic functionality for classes representing
12 | * player classes
13 | *
14 | * @author Sebastian Staudt
15 | */
16 | public abstract class GameClass {
17 |
18 | protected String name;
19 |
20 | protected int playTime;
21 |
22 | /**
23 | * Returns the name of this class
24 | *
25 | * @return [String] The name of this class
26 | */
27 | public String getName() {
28 | return this.name;
29 | }
30 |
31 | /**
32 | * Returns the time in minutes the player has played with this class
33 | *
34 | * @return The time this class has been played
35 | */
36 | public int getPlayTime() {
37 | return this.playTime;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/exceptions/WebApiExceptionTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.exceptions;
9 |
10 | import org.junit.Test;
11 |
12 | import static org.hamcrest.CoreMatchers.is;
13 | import static org.hamcrest.CoreMatchers.equalTo;
14 | import static org.junit.Assert.assertThat;
15 |
16 | /**
17 | * @author Sebastian Staudt
18 | */
19 | public class WebApiExceptionTest {
20 |
21 | @Test
22 | public void testInvalidKey() {
23 | WebApiException exception = new WebApiException(WebApiException.Cause.INVALID_KEY);
24 | assertThat(exception.getMessage(), is(equalTo("This is not a valid Steam Web API key.")));
25 | }
26 |
27 | @Test
28 | public void testSimple() {
29 | WebApiException exception = new WebApiException("message");
30 | assertThat(exception.getMessage(), is(equalTo("message")));
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONAuthRequestPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | /**
11 | * This packet class represents a SERVERDATA_AUTH request sent to a Source
12 | * server
13 | *
14 | * It is used to authenticate the client for RCON communication.
15 | *
16 | * @author Sebastian Staudt
17 | * @see com.github.koraktor.steamcondenser.servers.SourceServer#rconAuth
18 | */
19 | public class RCONAuthRequestPacket extends RCONPacket {
20 |
21 | /**
22 | * Creates a RCON authentication request for the given request ID and RCON
23 | * password
24 | *
25 | * @param requestId The request ID of the RCON connection
26 | * @param rconPassword The RCON password of the server
27 | */
28 | public RCONAuthRequestPacket(int requestId, String rconPassword) {
29 | super(requestId, RCONPacket.SERVERDATA_AUTH, rconPassword);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONExecRequestPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | /**
11 | * This packet class represents a SERVERDATA_EXECCOMMAND packet sent to a
12 | * Source server
13 | *
14 | * It is used to request a command execution on the server.
15 | *
16 | * @author Sebastian Staudt
17 | * @see com.github.koraktor.steamcondenser.servers.SourceServer#rconExec
18 | */
19 | public class RCONExecRequestPacket extends RCONPacket {
20 |
21 | /**
22 | * Creates a RCON command execution request for the given request ID and
23 | * command
24 | *
25 | * @param requestId The request ID of the RCON connection
26 | * @param rconCommand The command to execute on the server
27 | */
28 | public RCONExecRequestPacket(int requestId, String rconCommand) {
29 | super(requestId, RCONPacket.SERVERDATA_EXECCOMMAND, rconCommand);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONAuthResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | /**
11 | * This packet class represents a SERVERDATA_AUTH_RESPONSE packet sent by a
12 | * Source server
13 | *
14 | * It is used to indicate the success or failure of an authentication attempt
15 | * of a client for RCON communication.
16 | *
17 | * @author Sebastian Staudt
18 | * @see com.github.koraktor.steamcondenser.servers.SourceServer#rconAuth
19 | */
20 | public class RCONAuthResponse extends RCONPacket {
21 |
22 | /**
23 | * Creates a RCON authentication response for the given request ID
24 | *
25 | * The request ID of the packet will match the client's request if
26 | * authentication was successful
27 | *
28 | * @param requestId The request ID of the RCON connection
29 | */
30 | public RCONAuthResponse(int requestId) {
31 | super(requestId, RCONPacket.SERVERDATA_AUTH_RESPONSE, "");
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONTerminator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | /**
11 | * This packet class represents a special SERVERDATA_RESPONSE_VALUE packet
12 | * which is sent to the server
13 | *
14 | * It is used to determine the end of a RCON response from Source servers.
15 | * Packets of this type are sent after the actual RCON command and the empty
16 | * response packet from the server will indicate the end of the response.
17 | *
18 | * @author Sebastian Staudt
19 | * @see com.github.koraktor.steamcondenser.servers.SourceServer#rconExec
20 | */
21 | public class RCONTerminator extends RCONPacket {
22 |
23 | /**
24 | * Creates a new RCON terminator packet instance for the given request ID
25 | *
26 | * @param requestId The request ID for the current RCON communication
27 | */
28 | public RCONTerminator(int requestId) {
29 | super(requestId, RCONPacket.SERVERDATA_RESPONSE_VALUE, "");
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/S2A_INFO_BasePacket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import java.util.HashMap;
11 |
12 | /**
13 | * This module implements methods to generate and access server information
14 | * from S2A_INFO_DETAILED and S2A_INFO2 response packets
15 | *
16 | * @author Sebastian Staudt
17 | * @see S2A_INFO_DETAILED_Packet
18 | * @see S2A_INFO2_Packet
19 | */
20 | public abstract class S2A_INFO_BasePacket extends SteamPacket {
21 |
22 | protected HashMap info;
23 |
24 | S2A_INFO_BasePacket(byte headerByte, byte[] dataBytes) {
25 | super(headerByte, dataBytes);
26 |
27 | this.info = new HashMap<>();
28 | }
29 |
30 | /**
31 | * Returns a generated array of server properties from the instance
32 | * variables of the packet object
33 | *
34 | * @return The information provided by the server
35 | */
36 | public HashMap getInfo() {
37 | return this.info;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Sniper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * Represents the stats for the Team Fortress 2 Sniper class for a specific
14 | * user
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class TF2Sniper extends TF2Class {
19 |
20 | private int maxHeadshots;
21 |
22 | /**
23 | * Creates a new instance of the Sniper class based on the given XML data
24 | *
25 | * @param classData The XML data for this Sniper
26 | */
27 | public TF2Sniper(XMLData classData) {
28 | super(classData);
29 |
30 | this.maxHeadshots = classData.getInteger("iheadshots");
31 | }
32 |
33 | /**
34 | * Returns the maximum number enemies killed with a headshot by the player
35 | * in single life as a Sniper
36 | *
37 | * @return Maximum number of headshots
38 | */
39 | public int getMaxHeadshots() {
40 | return this.maxHeadshots;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/A2S_RULES_Packet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import com.github.koraktor.steamcondenser.Helper;
11 |
12 | /**
13 | * This packet class represents a A2S_RULES request send to a game server
14 | *
15 | * The game server will return a list of currently active game rules, e.g.
16 | * mp_friendlyfire = 1.
17 | *
18 | * This packet type requires the client to challenge the server in advance,
19 | * which is done automatically if required.
20 | *
21 | * @author Sebastian Staudt
22 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateRules
23 | */
24 | public class A2S_RULES_Packet extends SteamPacket {
25 |
26 | /**
27 | * Creates a new A2S_RULES request object including the challenge number
28 | *
29 | * @param challengeNumber The challenge number received from the server
30 | */
31 | public A2S_RULES_Packet(int challengeNumber) {
32 | super(SteamPacket.A2S_RULES_HEADER, Helper.byteArrayFromInteger(Integer.reverseBytes(challengeNumber)));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/community/portal2/Portal2StatsTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011, Guto Maia
6 | * Copyright (c) 2011-2012, Sebastian Staudt
7 | */
8 |
9 | package com.github.koraktor.steamcondenser.community.portal2;
10 |
11 | import org.junit.Test;
12 |
13 | import com.github.koraktor.steamcondenser.community.GameStatsTestCase;
14 |
15 | import static org.junit.Assert.assertEquals;
16 |
17 | /**
18 | * @author Guto Maia
19 | * @author Sebastian Staudt
20 | */
21 | public class Portal2StatsTest extends GameStatsTestCase {
22 |
23 | public Portal2StatsTest() {
24 | super("gutomaia", "portal2");
25 | }
26 |
27 | @Test
28 | public void getPortal2Stats() throws Exception {
29 | assertEquals("Portal 2", stats.getGame().getName());
30 | assertEquals("portal2", stats.getGame().getShortName());
31 | assertEquals(620, stats.getGame().getAppId());
32 | assertEquals("0", stats.getHoursPlayed());
33 | assertEquals("gutomaia", stats.getUser().getCustomUrl());
34 | }
35 |
36 | @Test
37 | public void achievements() throws Exception {
38 | assertEquals(17, stats.getAchievementsDone());
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/exceptions/SteamCondenserException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.exceptions;
9 |
10 | /**
11 | * This exception class is used as a base class for all exceptions related to
12 | * Steam Condenser's operation
13 | *
14 | * @author Sebastian Staudt
15 | */
16 | public class SteamCondenserException extends Exception {
17 |
18 | /**
19 | * Creates a new SteamCondenserException instance
20 | */
21 | public SteamCondenserException() {}
22 |
23 | /**
24 | * Creates a new SteamCondenserException instance
25 | *
26 | * @param message The message to attach to the exception
27 | */
28 | public SteamCondenserException(String message) {
29 | super(message);
30 | }
31 |
32 | /**
33 | * Creates a new SteamCondenserException instance
34 | *
35 | * @param message The message to attach to the exception
36 | * @param cause The initial error that caused this exception
37 | */
38 | public SteamCondenserException(String message, Throwable cause) {
39 | super(message, cause);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/L4DExplosive.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import com.github.koraktor.steamcondenser.community.GameWeapon;
11 | import com.github.koraktor.steamcondenser.community.XMLData;
12 |
13 | /**
14 | * This class represents the statistics of a single explosive weapon for a user
15 | * in Left4Dead
16 | *
17 | * @author Sebastian Staudt
18 | */
19 | public class L4DExplosive extends GameWeapon {
20 |
21 | /**
22 | * Creates a new instance of an explosivve based on the given XML data
23 | *
24 | * @param weaponData The XML data of this explosive
25 | */
26 | public L4DExplosive(XMLData weaponData) {
27 | super(weaponData);
28 |
29 | this.id = weaponData.getName();
30 | this.shots = weaponData.getInteger("thrown");
31 | }
32 |
33 | /**
34 | * Returns the average number of killed zombies for one shot of this
35 | * explosive
36 | *
37 | * @return The average number of kills per shot
38 | */
39 | public float getAvgKillsPerShot() {
40 | return 1 / this.getAvgShotsPerKill();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/S2C_CHALLENGE_Packet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | /**
11 | * This packet class represents a S2C_CHALLENGE response replied by a game
12 | * server
13 | *
14 | * It is used to provide a challenge number to a client requesting information
15 | * from the game server.
16 | *
17 | * @author Sebastian Staudt
18 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateChallengeNumber
19 | */
20 | public class S2C_CHALLENGE_Packet extends SteamPacket {
21 |
22 | /**
23 | * Creates a new S2C_CHALLENGE response object based on the given data
24 | *
25 | * @param challengeNumberBytes The raw packet data replied from the server
26 | */
27 | public S2C_CHALLENGE_Packet(byte[] challengeNumberBytes) {
28 | super(SteamPacket.S2C_CHALLENGE_HEADER, challengeNumberBytes);
29 | }
30 |
31 | /**
32 | * Returns the challenge number received from the game server
33 | *
34 | * @return The challenge number provided by the game server
35 | */
36 | public int getChallengeNumber() {
37 | return Integer.reverseBytes(this.contentData.getInt());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONGoldSrcResponsePacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
11 |
12 | /**
13 | * This packet class represents a RCON response packet sent by a GoldSrc server
14 | *
15 | * It is used to transport the output of a command from the server to the
16 | * client which requested the command execution.
17 | *
18 | * @author Sebastian Staudt
19 | * @see com.github.koraktor.steamcondenser.servers.GoldSrcServer#rconExec
20 | */
21 | public class RCONGoldSrcResponsePacket extends SteamPacket {
22 |
23 | /**
24 | * Creates a RCON command response for the given command output
25 | *
26 | * @param commandResponse The output of the command executed on the server
27 | */
28 | public RCONGoldSrcResponsePacket(byte[] commandResponse) {
29 | super(SteamPacket.RCON_GOLDSRC_RESPONSE_HEADER, commandResponse);
30 | }
31 |
32 | /**
33 | * Returns the output of the command execution
34 | *
35 | * @return The output of the command
36 | */
37 | public String getResponse() {
38 | return this.contentData.getString();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/Helper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser;
9 |
10 | /**
11 | * A helper class used to convert byte arrays into integers and vice-versa
12 | *
13 | * @author Sebastian Staudt
14 | */
15 | public abstract class Helper {
16 |
17 | /**
18 | * Convert an integer value into the corresponding byte array
19 | *
20 | * @param integer The integer to convert
21 | * @return The byte array representing the given integer
22 | */
23 | public static byte[] byteArrayFromInteger(int integer) {
24 | return new byte[] {
25 | (byte) (integer >> 24),
26 | (byte) (integer >> 16),
27 | (byte) (integer >> 8),
28 | (byte) integer
29 | };
30 | }
31 |
32 | /**
33 | * Convert a byte array into the corresponding integer value of its bytes
34 | *
35 | * @param byteArray The byte array to convert
36 | * @return The integer represented by the byte array
37 | */
38 | public static int integerFromByteArray(byte[] byteArray) {
39 | return byteArray[0] << 24 |
40 | (byteArray[1] & 0xff) << 16 |
41 | (byteArray[2] & 0xff) << 8 |
42 | (byteArray[3] & 0xff);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/A2S_PLAYER_Packet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import com.github.koraktor.steamcondenser.Helper;
11 |
12 | /**
13 | * This packet class represents a A2S_PLAYER request send to a game server
14 | *
15 | * It is used to request the list of players currently playing on the server.
16 | *
17 | * This packet type requires the client to challenge the server in advance,
18 | * which is done automatically if required.
19 | *
20 | * @author Sebastian Staudt
21 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updatePlayers
22 | */
23 | public class A2S_PLAYER_Packet extends SteamPacket {
24 |
25 | /**
26 | * Creates a new A2S_PLAYER request object without a challenge number
27 | */
28 | public A2S_PLAYER_Packet() {
29 | this(-1);
30 | }
31 |
32 | /**
33 | * Creates a new A2S_PLAYER request object including the challenge number
34 | *
35 | * @param challengeNumber The challenge number received from the server
36 | */
37 | public A2S_PLAYER_Packet(int challengeNumber) {
38 | super(SteamPacket.A2S_PLAYER_HEADER, Helper.byteArrayFromInteger(Integer.reverseBytes(challengeNumber)));
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2020, Sebastian Staudt
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 | * Neither the name of the author nor the names of its contributors
13 | may be used to endorse or promote products derived from this software
14 | without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/dota2/Dota2Item.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.dota2;
9 |
10 | import org.json.JSONObject;
11 |
12 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
13 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
14 | import com.github.koraktor.steamcondenser.community.GameItem;
15 |
16 | /**
17 | * Represents a DotA 2 item
18 | *
19 | * @author Sebastian Staudt
20 | */
21 | public class Dota2Item extends GameItem {
22 |
23 | private boolean equipped;
24 |
25 | /**
26 | * Creates a new instance of a Dota2Item with the given data
27 | *
28 | * @param inventory The inventory this item is contained in
29 | * @param itemData The data specifying this item
30 | * @throws WebApiException on Web API errors
31 | */
32 | public Dota2Item(Dota2Inventory inventory, JSONObject itemData)
33 | throws SteamCondenserException {
34 | super(inventory, itemData);
35 |
36 | this.equipped = !itemData.isNull("equipped");
37 | }
38 |
39 | /**
40 | * Returns whether this item is equipped by this player at all
41 | *
42 | * @return Whether this item is equipped by this player at all
43 | */
44 | public boolean isEquipped() {
45 | return this.equipped;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONExecResponsePacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | /**
11 | * This packet class represents a SERVERDATA_RESPONSE_VALUE packet sent by a
12 | * Source server
13 | *
14 | * It is used to transport the output of a command from the server to the
15 | * client which requested the command execution.
16 | *
17 | * @author Sebastian Staudt
18 | * @see com.github.koraktor.steamcondenser.servers.SourceServer#rconExec
19 | */
20 | public class RCONExecResponsePacket extends RCONPacket {
21 |
22 | /**
23 | * Creates a RCON command response for the given request ID and command
24 | * output
25 | *
26 | * @param requestId The request ID of the RCON connection
27 | * @param commandReturn The output of the command executed on the server
28 | */
29 | public RCONExecResponsePacket(int requestId, String commandReturn) {
30 | super(requestId, RCONPacket.SERVERDATA_RESPONSE_VALUE, commandReturn);
31 | }
32 |
33 | /**
34 | * Returns the output of the command execution
35 | *
36 | * @return The output of the command
37 | */
38 | public String getResponse() {
39 | String response = new String(this.contentData.array());
40 | return response.substring(0, response.length() - 2);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Medic.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * Represents the stats for the Team Fortress 2 Medic class for a specific user
14 | *
15 | * @author Sebastian Staudt
16 | */
17 | public class TF2Medic extends TF2Class {
18 |
19 | private int maxHealthHealed;
20 |
21 | private int maxUbercharges;
22 |
23 | /**
24 | * Creates a new instance of the Medic class based on the given XML data
25 | *
26 | * @param classData The XML data for this Medic
27 | */
28 | public TF2Medic(XMLData classData) {
29 | super(classData);
30 |
31 | this.maxHealthHealed = classData.getInteger("ihealthpointshealed");
32 | this.maxUbercharges = classData.getInteger("inuminvulnerable");
33 | }
34 |
35 | /**
36 | * Returns the maximum health healed for teammates by the player in a
37 | * single life as a Medic
38 | *
39 | * @return Maximum health healed
40 | */
41 | public int getMaxHealthHealed() {
42 | return this.maxHealthHealed;
43 | }
44 |
45 | /**
46 | * Returns the maximum number of ÜberCharges provided by the player in a
47 | * single life as a Medic
48 | *
49 | * @return Maximum number of ÜberCharges
50 | */
51 | public int getMaxUbercharges() {
52 | return this.maxUbercharges;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/L4D2Weapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * This class represents the statistics of a single weapon for a user in
14 | * Left4Dead 2
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class L4D2Weapon extends AbtractL4DWeapon {
19 |
20 | private int damage;
21 |
22 | private String weaponGroup;
23 |
24 | /**
25 | * Creates a new instance of a weapon based on the given XML data
26 | *
27 | * @param weaponData The XML data of this weapon
28 | */
29 | public L4D2Weapon(XMLData weaponData) {
30 | super(weaponData);
31 |
32 | this.damage = weaponData.getInteger("damage");
33 | this.killPercentage = Float.parseFloat(weaponData
34 | .getString("pctkills").replace("%", "")) * 0.01f;
35 | this.weaponGroup = weaponData.getAttribute("group");
36 | }
37 |
38 | /**
39 | * Returns the amount of damage done by the player with this weapon
40 | *
41 | * @return The damage done by this weapon
42 | */
43 | public int getDamage() {
44 | return this.damage;
45 | }
46 |
47 | /**
48 | * Returns the weapon group this weapon belongs to
49 | *
50 | * @return The group this weapon belongs to
51 | */
52 | public String getWeaponGroup() {
53 | return this.weaponGroup;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2ClassFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * The TF2ClassFactory is used to created instances of
14 | * TF2Class based on the XML input data
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | abstract class TF2ClassFactory
19 | {
20 | /**
21 | * Creates a new instance of a TF2 class instance based on the given XML
22 | * data
23 | *
24 | * This returns an instance of TF2Class or its subclasses
25 | * TF2Engineer, TF2Medic, TF2Sniper
26 | * or TF2Spy depending on the given XML data.
27 | *
28 | * @param classData The XML data for the class
29 | * @return The statistics for the given class data
30 | */
31 | public static TF2Class getTF2Class(XMLData classData) {
32 | String className = classData.getString("className");
33 |
34 | if(className.equals("Engineer")) {
35 | return new TF2Engineer(classData);
36 | } else if(className.equals("Medic")) {
37 | return new TF2Medic(classData);
38 | } else if(className.equals("Sniper")) {
39 | return new TF2Sniper(classData);
40 | } else if(className.equals("Spy")) {
41 | return new TF2Spy(classData);
42 | } else {
43 | return new TF2Class(classData);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONGoldSrcRequestPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | import com.github.koraktor.steamcondenser.Helper;
11 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
12 |
13 | /**
14 | * This packet class represents a RCON request packet sent to a GoldSrc server
15 | *
16 | * It is used to request a command execution on the server.
17 | *
18 | * @author Sebastian Staudt
19 | * @see com.github.koraktor.steamcondenser.servers.GoldSrcServer#rconExec
20 | */
21 | public class RCONGoldSrcRequestPacket extends SteamPacket {
22 | /**
23 | * Creates a request for the given request string
24 | *
25 | * The request string has the form rcon {challenge number} {RCON
26 | * password} {command}.
27 | *
28 | * @param request The request string to send to the server
29 | */
30 | public RCONGoldSrcRequestPacket(String request) {
31 | super((byte) 0, request.getBytes());
32 | }
33 |
34 | /**
35 | * Returns the raw data representing this packet
36 | *
37 | * @return A byte array containing the raw data of this request packet
38 | */
39 | public byte[] getBytes() {
40 | byte[] bytes = new byte[this.contentData.getLength() + 4];
41 |
42 | System.arraycopy(Helper.byteArrayFromInteger(0xFFFFFFFF), 0, bytes, 0, 4);
43 | System.arraycopy(this.contentData.array(), 0, bytes, 4, this.contentData.getLength());
44 |
45 | return bytes;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/GameWeapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community;
9 |
10 | /**
11 | * An abstract class implementing basic functionality for classes representing
12 | * game weapons
13 | *
14 | * @author Sebastian Staudt
15 | */
16 | public abstract class GameWeapon {
17 |
18 | protected int kills;
19 |
20 | protected String id;
21 |
22 | protected int shots;
23 |
24 | /**
25 | * Creates a new game weapon instance with the data provided
26 | *
27 | * @param weaponData The data representing this weapon
28 | */
29 | public GameWeapon(XMLData weaponData) {
30 | this.kills = weaponData.getInteger("kills");
31 | }
32 |
33 | /**
34 | * Returns the average number of shots needed for a kill with this weapon
35 | *
36 | * @return The average number of shots needed for a kill
37 | */
38 | public float getAvgShotsPerKill() {
39 | return this.shots / this.kills;
40 | }
41 |
42 | /**
43 | * Returns the unique identifier for this weapon
44 | *
45 | * @return The identifier of this weapon
46 | */
47 | public String getId() {
48 | return this.id;
49 | }
50 |
51 | /**
52 | * Returns the number of kills achieved with this weapon
53 | *
54 | * @return The number of kills achieved
55 | */
56 | public int getKills() {
57 | return this.kills;
58 | }
59 |
60 | /**
61 | * Returns the number of shots fired with this weapon
62 | *
63 | * @return The number of shots fired
64 | */
65 | public int getShots() {
66 | return this.shots;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/community/GameStatsTestCase.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011, Guto Maia
6 | * Copyright (c) 2011-2013, Sebastian Staudt
7 | */
8 |
9 | package com.github.koraktor.steamcondenser.community;
10 |
11 | import javax.xml.parsers.DocumentBuilder;
12 | import javax.xml.parsers.DocumentBuilderFactory;
13 |
14 | import org.junit.After;
15 | import org.junit.Before;
16 |
17 | import org.w3c.dom.Document;
18 |
19 | import static org.mockito.Mockito.mock;
20 | import static org.mockito.Mockito.verify;
21 | import static org.mockito.Mockito.when;
22 |
23 | /**
24 | * @author Guto Maia
25 | * @author Sebastian Staudt
26 | */
27 | public abstract class GameStatsTestCase {
28 |
29 | private String game;
30 |
31 | private DocumentBuilder parser;
32 |
33 | protected STATS stats;
34 |
35 | private String user;
36 |
37 | public GameStatsTestCase(String user, String game){
38 | this.game = game;
39 | this.user = user;
40 | }
41 |
42 | @Before
43 | @SuppressWarnings("unchecked")
44 | public void setUp() throws Exception {
45 | this.parser = mock(DocumentBuilder.class);
46 | XMLData.setDocumentBuilder(this.parser);
47 | Document statsDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(this.getClass().getResourceAsStream(this.user + "-" + this.game + ".xml"));
48 | when(parser.parse("http://steamcommunity.com/id/" + this.user + "/stats/" + this.game + "?xml=all")).thenReturn(statsDocument);
49 |
50 | this.stats = (STATS) GameStats.createGameStats(this.user, this.game);
51 | }
52 |
53 | @After
54 | public void tearDown() throws Exception{
55 | verify(parser).parse("http://steamcommunity.com/id/"+ this.user + "/stats/" + this.game + "?xml=all");
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/ServerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers;
9 |
10 | import java.net.InetAddress;
11 | import java.net.UnknownHostException;
12 | import java.util.ArrayList;
13 |
14 | import org.junit.Before;
15 | import org.junit.Test;
16 |
17 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertFalse;
21 | import static org.junit.Assert.assertTrue;
22 | import static org.mockito.Mockito.spy;
23 | import static org.mockito.Mockito.times;
24 | import static org.mockito.Mockito.verify;
25 |
26 | /**
27 | * @author Sebastian Staudt
28 | */
29 | public class ServerTest {
30 |
31 | private Server server;
32 |
33 | @Before
34 | public void setup() throws Exception {
35 | this.server = spy(new GenericServer());
36 | }
37 |
38 | @Test
39 | public void testRotateIp() throws Exception {
40 | this.server.ipAddresses.add(InetAddress.getByAddress(new byte[] { 127, 0, 0, 2 }));
41 |
42 | assertFalse(this.server.rotateIp());
43 | assertEquals(this.server.ipAddresses.get(1), this.server.ipAddress);
44 | assertTrue(this.server.rotateIp());
45 | assertEquals(this.server.ipAddresses.get(0), this.server.ipAddress);
46 |
47 | verify(this.server, times(2)).initSocket();
48 | }
49 |
50 | class GenericServer extends Server {
51 |
52 | public GenericServer() throws SteamCondenserException, UnknownHostException {
53 | super(InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }), 27015);
54 | }
55 |
56 | public void initSocket() {}
57 |
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONPacketFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | import com.github.koraktor.steamcondenser.PacketBuffer;
11 | import com.github.koraktor.steamcondenser.exceptions.PacketFormatException;
12 |
13 | /**
14 | * This module provides functionality to handle raw packet data for Source RCON
15 | *
16 | * It's is used to transform data bytes into packet objects for RCON
17 | * communication with Source servers.
18 | *
19 | * @author Sebastian Staudt
20 | * @see RCONPacket
21 | */
22 | public abstract class RCONPacketFactory {
23 |
24 | /**
25 | * Creates a new packet object based on the header byte of the given raw
26 | * data
27 | *
28 | * @param rawData The raw data of the packet
29 | * @return RCONPacket The packet object generated from the packet data
30 | * @throws PacketFormatException if the packet header is not recognized
31 | */
32 | public static RCONPacket getPacketFromData(byte[] rawData)
33 | throws PacketFormatException {
34 | PacketBuffer packetBuffer = new PacketBuffer(rawData);
35 |
36 | int requestId = Integer.reverseBytes(packetBuffer.getInt());
37 | int header = Integer.reverseBytes(packetBuffer.getInt());
38 | String data = packetBuffer.getString();
39 |
40 | switch(header) {
41 | case RCONPacket.SERVERDATA_AUTH_RESPONSE:
42 | return new RCONAuthResponse(requestId);
43 | case RCONPacket.SERVERDATA_RESPONSE_VALUE:
44 | return new RCONExecResponsePacket(requestId, data);
45 | default:
46 | throw new PacketFormatException("Unknown packet with header " + header + " received.");
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/portal2/Portal2Stats.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.portal2;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
12 | import com.github.koraktor.steamcondenser.community.GameInventory;
13 | import com.github.koraktor.steamcondenser.community.GameStats;
14 |
15 | /**
16 | * The Portal2Stats class represents the game statistics for a single user in
17 | * Portal 2
18 | *
19 | * @author Sebastian Staudt
20 | */
21 | public class Portal2Stats extends GameStats {
22 |
23 | private Portal2Inventory inventory;
24 |
25 | /**
26 | * Creates a new object holding Portal 2 statistics for the given user
27 | *
28 | * @param steamId The custom URL or 64bit Steam ID of the user
29 | * @throws SteamCondenserException if an error occurs while fetching the
30 | * stats data
31 | */
32 | public Portal2Stats(Object steamId) throws SteamCondenserException {
33 | super(steamId, "portal2");
34 | }
35 |
36 | /**
37 | * Returns the current Portal 2 inventory (a.k.a. Robot Enrichment) of this
38 | * player
39 | *
40 | * @return This player's Portal 2 inventory
41 | * @throws WebApiException if an error occurs while querying Steam's Web
42 | * API
43 | */
44 | public Portal2Inventory getInventory() throws SteamCondenserException {
45 | if(!this.isPublic()) {
46 | return null;
47 | }
48 |
49 | if(this.inventory == null) {
50 | this.inventory = (Portal2Inventory) GameInventory.create(Portal2Inventory.APP_ID, this.user.getSteamId64());
51 | }
52 |
53 | return this.inventory;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Spy.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * Represents the stats for the Team Fortress 2 Spy class for a specific user
14 | *
15 | * @author Sebastian Staudt
16 | */
17 | public class TF2Spy extends TF2Class {
18 |
19 | private int maxBackstabs;
20 |
21 | private int maxHeadShots;
22 |
23 | private int maxHealthLeeched;
24 |
25 | /**
26 | * Creates a new instance of the Spy class based on the given XML data
27 | *
28 | * @param classData The XML data for this Spy
29 | */
30 | public TF2Spy(XMLData classData) {
31 | super(classData);
32 |
33 | this.maxBackstabs = classData.getInteger("ibackstabs");
34 | this.maxHeadShots = classData.getInteger("iheadshots");
35 | this.maxHealthLeeched = classData.getInteger("ihealthpointsleached");
36 | }
37 |
38 | /**
39 | * Returns the maximum health leeched from enemies by the player in a single
40 | * life as a Spy
41 | *
42 | * @return Maximum health leeched
43 | */
44 | public int getMaxBackstabs() {
45 | return this.maxBackstabs;
46 | }
47 |
48 | /**
49 | * Returns the head shots by the player in a single life as a Spy
50 | *
51 | * @return Maximum number of head shots
52 | */
53 | public int getMaxHeadShots() {
54 | return this.maxHeadShots;
55 | }
56 |
57 | /**
58 | * Returns the maximum health leeched from enemies by the player in a single
59 | * life as a Spy
60 | *
61 | * @return Maximum health leeched
62 | */
63 | public int getMaxHealthLeeched() {
64 | return this.maxHealthLeeched;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Engineer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * Represents the stats for the Team Fortress 2 Engineer class for a specific
14 | * user
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class TF2Engineer extends TF2Class {
19 |
20 | private int maxBuildingsBuilt;
21 |
22 | private int maxSentryKills;
23 |
24 | private int maxTeleports;
25 |
26 | /**
27 | * Creates a new instance of the Engineer class based on the given XML data
28 | *
29 | * @param classData The XML data for this Engineer
30 | */
31 | public TF2Engineer(XMLData classData) {
32 | super(classData);
33 |
34 | this.maxBuildingsBuilt = classData.getInteger("ibuildingsbuilt");
35 | this.maxSentryKills = classData.getInteger("isentrykills");
36 | this.maxTeleports = classData.getInteger("inumteleports");
37 | }
38 |
39 | /**
40 | * Returns the maximum number of buildings built by the player in a single
41 | * life as an Engineer
42 | *
43 | * @return Maximum number of buildings built
44 | */
45 | public int getMaxBuildingsBuilt() {
46 | return this.maxBuildingsBuilt;
47 | }
48 |
49 | /**
50 | * Returns the maximum number of enemies killed by sentry guns built by the
51 | * player in a single life as an Engineer
52 | *
53 | * @return Maximum number of sentry kills
54 | */
55 | public int getMaxSentryKills() {
56 | return this.maxSentryKills;
57 | }
58 |
59 | /**
60 | * Returns the maximum number of teammates teleported by teleporters built
61 | * by the player in a single life as an Engineer
62 | *
63 | * @return Maximum number of teleports
64 | */
65 | public int getMaxTeleports() {
66 | return this.maxTeleports;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/S2A_RULES_Packet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import java.util.HashMap;
11 |
12 | import com.github.koraktor.steamcondenser.exceptions.PacketFormatException;
13 |
14 | /**
15 | * This class represents a S2A_RULES response sent by a game server
16 | *
17 | * It is used to transfer a list of server rules (a.k.a. CVARs) with their
18 | * active values.
19 | *
20 | * @author Sebastian Staudt
21 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateRules
22 | */
23 | public class S2A_RULES_Packet extends SteamPacket {
24 |
25 | private HashMap rulesHash;
26 |
27 | /**
28 | * Creates a new S2A_RULES response object based on the given data
29 | *
30 | * @param dataBytes The raw packet data sent by the server
31 | */
32 | public S2A_RULES_Packet(byte[] dataBytes)
33 | throws PacketFormatException {
34 | super(SteamPacket.S2A_RULES_HEADER, dataBytes);
35 |
36 | if (this.contentData.getLength() == 0) {
37 | throw new PacketFormatException("Wrong formatted S2A_RULES response packet.");
38 | }
39 |
40 | int rulesCount = Short.reverseBytes(this.contentData.getShort());
41 | this.rulesHash = new HashMap<>(rulesCount);
42 |
43 | String rule;
44 | String value;
45 | for (int i = 0; i < rulesCount; i++) {
46 | rule = this.contentData.getString();
47 | value = this.contentData.getString();
48 |
49 | if(rule.equals("")) {
50 | break;
51 | }
52 |
53 | this.rulesHash.put(rule, value);
54 | }
55 | }
56 |
57 | /**
58 | * Returns the list of server rules (a.k.a. CVars) with the current values
59 | *
60 | * @return array A list of server rules
61 | */
62 | public HashMap getRulesHash() {
63 | return this.rulesHash;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/S2A_PLAYER_Packet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import java.util.HashMap;
11 |
12 | import com.github.koraktor.steamcondenser.exceptions.PacketFormatException;
13 | import com.github.koraktor.steamcondenser.servers.SteamPlayer;
14 |
15 | /**
16 | * This class represents a S2A_PLAYER response sent by a game server
17 | *
18 | * It is used to transfer a list of players currently playing on the server.
19 | *
20 | * @author Sebastian Staudt
21 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updatePlayers
22 | */
23 | public class S2A_PLAYER_Packet extends SteamPacket {
24 |
25 | private HashMap playerHash;
26 |
27 | /**
28 | * Creates a new S2A_PLAYER response object based on the given data
29 | *
30 | * @param dataBytes The raw packet data sent by the server
31 | */
32 | public S2A_PLAYER_Packet(byte[] dataBytes)
33 | throws PacketFormatException {
34 | super(SteamPacket.S2A_PLAYER_HEADER, dataBytes);
35 |
36 | if(this.contentData.getLength() == 0) {
37 | throw new PacketFormatException("Wrong formatted S2A_PLAYER response packet.");
38 | }
39 |
40 | this.playerHash = new HashMap<>(this.contentData.getByte());
41 |
42 | while(this.contentData.hasRemaining()) {
43 | int playerId = this.contentData.getByte() & 0xff;
44 | String playerName = this.contentData.getString();
45 | this.playerHash.put(playerName, new SteamPlayer(
46 | playerId,
47 | playerName,
48 | Integer.reverseBytes(this.contentData.getInt()),
49 | Float.intBitsToFloat(Integer.reverseBytes(this.contentData.getInt()))
50 | ));
51 | }
52 | }
53 |
54 | /**
55 | * Returns the list of active players provided by the server
56 | *
57 | * @return All active players on the server
58 | */
59 | public HashMap getPlayerHash() {
60 | return this.playerHash;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/community/l4d/L4D2StatsTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011, Guto Maia
6 | * Copyright (c) 2011-2012, Sebastian Staudt
7 | */
8 |
9 | package com.github.koraktor.steamcondenser.community.l4d;
10 |
11 | import org.junit.Test;
12 |
13 | import com.github.koraktor.steamcondenser.community.GameStatsTestCase;
14 |
15 | import static org.junit.Assert.assertEquals;
16 |
17 | /**
18 | * @author Guto Maia
19 | * @author Sebastian Staudt
20 | */
21 | public class L4D2StatsTest extends GameStatsTestCase {
22 |
23 | public L4D2StatsTest() {
24 | super("gutomaia", "l4d2");
25 | }
26 |
27 | @Test
28 | public void getL4D2Stats() throws Exception {
29 |
30 | assertEquals("Left 4 Dead 2", stats.getGame().getName());
31 | assertEquals("l4d2", stats.getGame().getShortName());
32 | assertEquals(550, stats.getGame().getAppId());
33 | assertEquals("0s", stats.getHoursPlayed());// TODO: strange behavior
34 | assertEquals("gutomaia", stats.getUser().getCustomUrl());
35 | }
36 |
37 | @Test
38 | public void achievements() throws Exception {
39 | assertEquals(7, stats.getAchievementsDone());
40 | }
41 |
42 | @Test
43 | public void getDamagePercentages() throws Exception {
44 | assertEquals("9.9", stats.getDamagePercentages().get("melee")
45 | .toString());
46 | assertEquals("28.8", stats.getDamagePercentages().get("pistols")
47 | .toString());
48 | assertEquals("43.8", stats.getDamagePercentages().get("rifles")
49 | .toString());
50 | assertEquals("17.0", stats.getDamagePercentages().get("shotguns")
51 | .toString());
52 | }
53 |
54 | @Test
55 | public void getLifetimeStats() {
56 | assertEquals("0.0", stats.getLifetimeStats().get("avgAdrenalineShared")
57 | .toString());
58 | assertEquals("0.263158",
59 | stats.getLifetimeStats().get("avgAdrenalineUsed").toString());
60 | assertEquals("0.157895",
61 | stats.getLifetimeStats().get("avgDefibrillatorsUsed")
62 | .toString());
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contribution Guidelines
2 | =======================
3 |
4 | First of all, each single contribution is appreciated, whether a typo fix,
5 | improved documentation, a fixed bug or a whole new feature.
6 |
7 | ## Making your changes
8 |
9 | 1. Fork the repository on GitHub
10 | 2. Create a topic branch with a descriptive name, e.g. `fix-issue-123` or
11 | `feature-x`
12 | 3. Make your modifications, complying with the
13 | [code conventions](#code-conventions)
14 | 4. Commit small logical changes, each with a descriptive commit message.
15 | Please don't mix unrelated changes in a single commit.
16 |
17 | ## Commit messages
18 |
19 | Please format your commit messages as follows:
20 |
21 | Short summary of the change (up to 50 characters)
22 |
23 | Optionally add a more extensive description of your change after a
24 | blank line. Wrap the lines in this and the following paragraphs after
25 | 72 characters.
26 |
27 | ## Submitting your changes
28 |
29 | 1. Push your changes to a topic branch in your fork of the repository.
30 | 2. [Submit a pull request][pr] to the original repository.
31 | Describe your changes as short as possible, but as detailed as needed for
32 | others to get an overview of your modifications.
33 | 3. *Optionally*, [open an issue][issue] in the meta-repository if your change
34 | might be relevant to other implementations of Steam Condenser. Please add a
35 | link to your pull request.
36 |
37 | ## Code conventions
38 |
39 | * White spaces:
40 | * Indent using 4 spaces
41 | * Line endings must be line feeds (\n)
42 | * Add a newline at end of file
43 | * Name conventions:
44 | * `UpperCamelCase` for classes, interfaces and enums
45 | * `lowerCamelCase` for fields, methods and variables
46 | * `UPPER_CASE` for constants and `final static` fields
47 |
48 | ## Further information
49 |
50 | * [General GitHub documentation][gh-help]
51 | * [GitHub pull request documentation][gh-pr]
52 | * [Google group][mail]
53 | * \#steam-condenser on freenode.net
54 |
55 | [gh-help]: https://help.github.com
56 | [gh-pr]: https://help.github.com/send-pull-requests
57 | [issue]: https://github.com/koraktor/steam-condenser/issues/new
58 | [mail]: https://groups.google.com/group/steam-condenser
59 | [pr]: https://github.com/koraktor/steam-condenser-java/pull/new
60 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/GameLeaderboardEntry.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 |
12 | /**
13 | * The GameLeaderboard class represents a single entry in a leaderboard
14 | *
15 | * @author Sebastian Staudt
16 | */
17 | public class GameLeaderboardEntry {
18 |
19 | protected SteamId steamId;
20 |
21 | protected int score;
22 |
23 | protected int rank;
24 |
25 | protected GameLeaderboard leaderboard;
26 |
27 | /**
28 | * Creates new entry instance for the given XML data and leaderboard
29 | *
30 | * @param entryData The XML data of the leaderboard of the leaderboard
31 | * entry
32 | * @param leaderboard The leaderboard this entry belongs to
33 | */
34 | public GameLeaderboardEntry(XMLData entryData, GameLeaderboard leaderboard) {
35 | try {
36 | this.steamId = SteamId.create(entryData.getString("steamid"), false);
37 | } catch(SteamCondenserException e) {}
38 | this.score = entryData.getInteger("score");
39 | this.rank = entryData.getInteger("rank");
40 | this.leaderboard = leaderboard;
41 | }
42 |
43 | /**
44 | * Returns the Steam ID of this entry's player
45 | *
46 | * @return The Steam ID of the player
47 | */
48 | public SteamId getSteamId() {
49 | return this.steamId;
50 | }
51 |
52 | /**
53 | * Returns the score of this entry
54 | *
55 | * @return The score of this player
56 | */
57 | public int getScore() {
58 | return this.score;
59 | }
60 |
61 | /**
62 | * Returns the rank where this entry is listed in the leaderboard
63 | *
64 | * @return The rank of this entry
65 | */
66 | public int getRank() {
67 | return this.rank;
68 | }
69 |
70 | /**
71 | * Returns the leaderboard this entry belongs to
72 | *
73 | * @return The leaderboard of this entry
74 | */
75 | public GameLeaderboard getLeaderboard() {
76 | return this.leaderboard;
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/alien_swarm/AlienSwarmWeapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.alien_swarm;
9 |
10 | import com.github.koraktor.steamcondenser.community.GameWeapon;
11 | import com.github.koraktor.steamcondenser.community.XMLData;
12 |
13 | /**
14 | * This class holds statistical information about weapons used by a player
15 | * in Alien Swarm
16 | *
17 | * @author Sebastian Staudt
18 | */
19 | public class AlienSwarmWeapon extends GameWeapon {
20 |
21 | private float accuracy;
22 |
23 | private int damage;
24 |
25 | private int friendlyFire;
26 |
27 | private String name;
28 |
29 | /**
30 | * Creates a new weapon instance based on the assigned weapon XML data
31 | *
32 | * @param weaponData The data representing this weapon
33 | */
34 | public AlienSwarmWeapon(XMLData weaponData) {
35 | super(weaponData);
36 |
37 | this.accuracy = weaponData.getFloat("accuracy");
38 | this.damage = weaponData.getInteger("damage");
39 | this.friendlyFire = weaponData.getInteger("friendlyfire");
40 | this.name = weaponData.getString("name");
41 | this.shots = weaponData.getInteger("shotsfired");
42 | }
43 |
44 | /**
45 | * Returns the accuracy of the player with this weapon
46 | *
47 | * @return The accuracy of the player with this weapon
48 | */
49 | public float getAccuracy() {
50 | return this.accuracy;
51 | }
52 |
53 | /**
54 | * Returns the damage achieved with this weapon
55 | *
56 | * @return The damage achieved with this weapon
57 | */
58 | public int getDamage() {
59 | return this.damage;
60 | }
61 |
62 | /**
63 | * Returns the damage dealt to team mates with this weapon
64 | *
65 | * @return The damage dealt to team mates with this weapon
66 | */
67 | public int getFriendlyFire() {
68 | return this.friendlyFire;
69 | }
70 |
71 | /**
72 | * Returns the name of this weapon
73 | *
74 | * @return The name of this weapon
75 | */
76 | public String getName() {
77 | return this.name;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/sockets/MasterServerSocket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.net.InetAddress;
11 | import java.util.concurrent.TimeoutException;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import com.github.koraktor.steamcondenser.exceptions.PacketFormatException;
16 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
17 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
18 |
19 | /**
20 | * This class represents a socket used to communicate with master servers
21 | *
22 | * @author Sebastian Staudt
23 | */
24 | public class MasterServerSocket extends QuerySocket {
25 |
26 | protected static final Logger LOG = LoggerFactory.getLogger(MasterServerSocket.class);
27 |
28 | /**
29 | * Creates a new socket to communicate with the server on the given IP
30 | * address and port
31 | *
32 | * @param ipAddress Either the IP address or the DNS name of the server
33 | * @param portNumber The port the server is listening on
34 | * @throws SteamCondenserException if the socket cannot be opened
35 | */
36 | public MasterServerSocket(InetAddress ipAddress, int portNumber)
37 | throws SteamCondenserException {
38 | super(ipAddress, portNumber);
39 | }
40 |
41 | /**
42 | * Reads a single packet from the socket
43 | *
44 | * @return The packet replied from the server
45 | * @throws SteamCondenserException if an error occurs while communicating
46 | * with the server
47 | * @throws PacketFormatException if the packet has the wrong format
48 | * @throws TimeoutException if the request times out
49 | */
50 | public SteamPacket getReply()
51 | throws SteamCondenserException, TimeoutException {
52 | this.receivePacket(1500);
53 |
54 | if(this.buffer.getInt() != -1) {
55 | throw new PacketFormatException("Master query response has wrong packet header.");
56 | }
57 |
58 | SteamPacket packet = this.getPacketFromData();
59 |
60 | LOG.info("Received reply of type \"" + packet.getClass().getSimpleName() + "\"");
61 |
62 | return packet;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/M2A_SERVER_BATCH_Packet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import java.util.Vector;
11 |
12 | import com.github.koraktor.steamcondenser.exceptions.PacketFormatException;
13 |
14 | /**
15 | * This packet class represents a M2A_SERVER_BATCH response replied by a master
16 | * server
17 | *
18 | * It contains a list of IP addresses and ports of game servers matching the
19 | * requested criteria.
20 | *
21 | * @author Sebastian Staudt
22 | * @see com.github.koraktor.steamcondenser.servers.MasterServer#getServers
23 | */
24 | public class M2A_SERVER_BATCH_Packet extends SteamPacket {
25 |
26 | private Vector serverArray;
27 |
28 | /**
29 | * Creates a new M2A_SERVER_BATCH response object based on the given data
30 | *
31 | * @param data The raw packet data replied from the server
32 | * @throws PacketFormatException if the packet data is not well formatted
33 | */
34 | public M2A_SERVER_BATCH_Packet(byte[] data)
35 | throws PacketFormatException {
36 | super(SteamPacket.M2A_SERVER_BATCH_HEADER, data);
37 |
38 | if(this.contentData.getByte() != 0x0A) {
39 | throw new PacketFormatException("Master query response is missing additional 0x0A byte.");
40 | }
41 |
42 | int firstOctet, secondOctet, thirdOctet, fourthOctet, portNumber;
43 | this.serverArray = new Vector<>();
44 |
45 | do {
46 | firstOctet = this.contentData.getByte() & 0xFF;
47 | secondOctet = this.contentData.getByte() & 0xFF;
48 | thirdOctet = this.contentData.getByte() & 0xFF;
49 | fourthOctet = this.contentData.getByte() & 0xFF;
50 | portNumber = this.contentData.getShort() & 0xFFFF;
51 |
52 | this.serverArray.add(firstOctet + "." + secondOctet + "." + thirdOctet + "." + fourthOctet + ":" + portNumber);
53 | } while(this.contentData.remaining() > 0);
54 | }
55 |
56 | /**
57 | * Returns the list of servers returned from the server in this packet
58 | *
59 | * @return An array of server addresses (i.e. IP addresses + port numbers)
60 | */
61 | public Vector getServers() {
62 | return this.serverArray;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Steam Condenser
2 | ===============
3 |
4 | [](http://travis-ci.org/koraktor/steam-condenser-java)
5 |
6 | The Steam Condenser is a multi-language library for querying the Steam
7 | Community, Source and GoldSrc game servers as well as the Steam master servers.
8 | Currently it is implemented in Java, PHP and Ruby.
9 |
10 | ## Requirements
11 |
12 | * Linux, MacOS X or Windows
13 | * Java 7 or newer
14 |
15 | The following Java libraries are required:
16 |
17 | * Apache Commons Compress (for Source servers sending compressed responses)
18 | * Apache Commons Lang 3
19 | * Apache Commons HttpClient (for the Web API features)
20 | * JSON (for the Web API features)
21 | * JUnit (for testing)
22 | * PowerMock (for testing)
23 |
24 | Maven will install these for you.
25 |
26 | ## Installation
27 |
28 | To install and use Steam Condenser in your Maven managed project use the
29 | following dependency definition:
30 |
31 |
32 | com.github.koraktor
33 | steam-condenser
34 | x.y.z
35 |
36 |
37 | Remember to specify a version using appropriate tag.
38 |
39 | ## Logging
40 |
41 | Steam Condenser provides logging based on [SLF4J][slf4j]. To make use of it you
42 | have to add a logger implementation (like slf4j-log4j) to your application's
43 | classpath. See [this list][loggers] for some available SLF4J loggers.
44 |
45 | ## License
46 |
47 | This code is free software; you can redistribute it and/or modify it under the
48 | terms of the new BSD License. A copy of this license can be found in the
49 | included LICENSE file.
50 |
51 | ## Credits
52 |
53 | * Sebastian Staudt – koraktor(at)gmail.com
54 | * David Wursteisen – david.wursteisen(at)gmail.com
55 | * Guto Maia – guto(at)guto.net
56 | * Sam Kinard – snkinard(at)gmail.com
57 |
58 | ## See Also
59 |
60 | * [Steam Condenser home](http://koraktor.de/steam-condenser)
61 | * [GitHub project page](https://github.com/koraktor/steam-condenser)
62 | * [Wiki](https://github.com/koraktor/steam-condenser/wiki)
63 | * [Google group](http://groups.google.com/group/steam-condenser)
64 | * [Ohloh profile](http://www.ohloh.net/projects/steam-condenser)
65 |
66 | Follow Steam Condenser on Google Plus+ via
67 | [+Steam Condenser](https://plus.google.com/b/109400543549250623875/109400543549250623875)
68 | or on Twitter via [@steamcondenser](https://twitter.com/steamcondenser).
69 |
70 | [loggers]: http://www.slf4j.org/manual.html#swapping
71 | [slf4j]: http://www.slf4j.org
72 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/portal2/Portal2Item.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.portal2;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 | import org.json.JSONException;
15 | import org.json.JSONObject;
16 |
17 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
18 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
19 | import com.github.koraktor.steamcondenser.community.GameItem;
20 |
21 | /**
22 | * Represents a Portal 2 item
23 | *
24 | * @author Sebastian Staudt
25 | */
26 | public class Portal2Item extends GameItem {
27 |
28 | private static final String[] BOTS = { "pbody", "atlas" };
29 |
30 | private Map equipped;
31 |
32 | /**
33 | * Creates a new instance of a Portal2Item with the given data
34 | *
35 | * @param inventory The inventory this item is contained in
36 | * @param itemData The data specifying this item
37 | * @throws JSONException on invalid JSON data
38 | * @throws WebApiException on Web API errors
39 | */
40 | public Portal2Item(Portal2Inventory inventory, JSONObject itemData)
41 | throws JSONException, SteamCondenserException {
42 | super(inventory, itemData);
43 |
44 | this.equipped = new HashMap<>();
45 | for(int botId = 0; botId < BOTS.length; botId++) {
46 | this.equipped.put(BOTS[botId], (itemData.getLong("inventory") & (1 << 16 + botId)) != 0);
47 | }
48 | }
49 |
50 | /**
51 | * Returns the name for each bot this player has equipped this item
52 | *
53 | * @return The names of the bots this player has equipped this item
54 | */
55 | public List getBotsEquipped() {
56 | List botsEquipped = new ArrayList<>();
57 | for(Map.Entry botEquipped : this.equipped.entrySet()) {
58 | if(botEquipped.getValue()) {
59 | botsEquipped.add(botEquipped.getKey());
60 | }
61 | }
62 |
63 | return botsEquipped;
64 | }
65 |
66 | /**
67 | * Returns whether this item is equipped by this player at all
68 | *
69 | * @return Whether this item is equipped by this player at all
70 | */
71 | public boolean isEquipped() {
72 | return this.equipped.containsValue(true);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Item.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 | import org.json.JSONException;
15 | import org.json.JSONObject;
16 |
17 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
18 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
19 | import com.github.koraktor.steamcondenser.community.GameItem;
20 |
21 | /**
22 | * Represents a Team Fortress 2 item
23 | *
24 | * @author Sebastian Staudt
25 | */
26 | public class TF2Item extends GameItem {
27 |
28 | private static final String[] CLASSES = {
29 | "scout", "sniper", "soldier", "demoman", "medic", "heavy", "pyro",
30 | "spy"
31 | };
32 |
33 | private Map equipped;
34 |
35 | /**
36 | * Creates a new instance of a TF2Item with the given data
37 | *
38 | * @param inventory The inventory this item is contained in
39 | * @param itemData The data specifying this item
40 | * @throws JSONException on invalid JSON data
41 | * @throws WebApiException on Web API errors
42 | */
43 | public TF2Item(TF2Inventory inventory, JSONObject itemData)
44 | throws JSONException, SteamCondenserException {
45 | super(inventory, itemData);
46 |
47 | this.equipped = new HashMap<>();
48 | for(int classId = 0; classId < CLASSES.length; classId++) {
49 | this.equipped.put(CLASSES[classId], (itemData.getLong("inventory") & (1 << 16 + classId)) != 0);
50 | }
51 | }
52 |
53 | /**
54 | * Returns the class names for each class this player has equipped this
55 | * item
56 | *
57 | * @return The names of the classes this player has equipped this item
58 | */
59 | public List getClassesEquipped() {
60 | List classesEquipped = new ArrayList<>();
61 | for(Map.Entry classEquipped : this.equipped.entrySet()) {
62 | if(classEquipped.getValue()) {
63 | classesEquipped.add(classEquipped.getKey());
64 | }
65 | }
66 |
67 | return classesEquipped;
68 | }
69 |
70 | /**
71 | * Returns whether this item is equipped by this player at all
72 | *
73 | * @return true if the player has equipped this item at all
74 | */
75 | public boolean isEquipped() {
76 | return this.equipped.containsValue(true);
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/dods/DoDSWeapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.dods;
9 |
10 | import com.github.koraktor.steamcondenser.community.GameWeapon;
11 | import com.github.koraktor.steamcondenser.community.XMLData;
12 |
13 | /**
14 | * Represents the stats for a Day of Defeat: Source weapon for a specific user
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class DoDSWeapon extends GameWeapon {
19 |
20 | private int headshots;
21 |
22 | private int hits;
23 |
24 | private String name;
25 |
26 | /**
27 | * Creates a new instance of a Day of Defeat: Source weapon based on the
28 | * given XML data
29 | *
30 | * @param weaponData The XML data of the class
31 | */
32 | public DoDSWeapon(XMLData weaponData) {
33 | super(weaponData);
34 |
35 | this.headshots = weaponData.getInteger("headshots");
36 | this.id = weaponData.getAttribute("key");
37 | this.name = weaponData.getString("name");
38 | this.shots = weaponData.getInteger("shotsfired");
39 | this.hits = weaponData.getInteger("shotshit");
40 | }
41 |
42 | /**
43 | * Returns the average number of hits needed for a kill with this weapon
44 | *
45 | * @return The average number of hits needed for a kill
46 | */
47 | public float getAvgHitsPerKill() {
48 | return this.hits / this.kills;
49 | }
50 |
51 | /**
52 | * Returns the percentage of headshots relative to the shots hit with this
53 | * weapon
54 | *
55 | * @return The percentage of headshots
56 | */
57 | public float getHeadshotPercentage() {
58 | return this.headshots / this.hits;
59 | }
60 |
61 | /**
62 | * Returns the number of headshots achieved with this weapon
63 | *
64 | * @return The number of headshots achieved
65 | */
66 | public int getHeadshots() {
67 | return this.headshots;
68 | }
69 |
70 | /**
71 | * Returns the percentage of hits relative to the shots fired with this
72 | * weapon
73 | *
74 | * @return The percentage of hits
75 | */
76 | public float getHitPercentage() {
77 | return this.hits / this.shots;
78 | }
79 |
80 | /**
81 | * Returns the number of hits achieved with this weapon
82 | *
83 | * @return The number of hits achieved
84 | */
85 | public int getHits() {
86 | return this.hits;
87 | }
88 |
89 | /**
90 | * Returns the name of this weapon
91 | *
92 | * @return The name of this weapon
93 | */
94 | public String getName() {
95 | return this.name;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/css/CSSMap.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.css;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * Represents the stats for a Counter-Strike: Source map for a specific user
14 | *
15 | * @author Sebastian Staudt
16 | */
17 | public class CSSMap {
18 |
19 | private boolean favorite;
20 |
21 | private String name;
22 |
23 | private int roundsPlayed;
24 |
25 | private int roundsLost;
26 |
27 | private int roundsWon;
28 |
29 | /**
30 | * Creates a new instance of a Counter-Strike: Source class based on the
31 | * given XML data
32 | *
33 | * @param mapName The name of the map
34 | * @param mapsData The XML data of all maps
35 | */
36 | public CSSMap(String mapName, XMLData mapsData) {
37 | this.name = mapName;
38 |
39 | this.favorite = mapsData.getString("favorite").equals(this.name);
40 | this.roundsPlayed = mapsData.getInteger(this.name + "_rounds");
41 | this.roundsWon = mapsData.getInteger(this.name + "_wins");
42 | this.roundsLost = this.roundsPlayed - this.roundsWon;
43 | }
44 |
45 | /**
46 | * Returns whether this map is the favorite map of this player
47 | *
48 | * @return true if this is the favorite map
49 | */
50 | public boolean isFavorite() {
51 | return this.favorite;
52 | }
53 |
54 | /**
55 | * Returns the name of this map
56 | *
57 | * @return The name of this map
58 | */
59 | public String getName() {
60 | return this.name;
61 | }
62 |
63 | /**
64 | * Returns the number of rounds the player has lost on this map
65 | *
66 | * @return The number of rounds lost
67 | */
68 | public int getRoundsLost() {
69 | return this.roundsLost;
70 | }
71 |
72 | /**
73 | * Returns the number of rounds the player has played on this map
74 | *
75 | * @return The number of rounds played
76 | */
77 | public int getRoundsPlayed() {
78 | return this.roundsPlayed;
79 | }
80 |
81 | /**
82 | * Returns the number of rounds the player has won on this map
83 | *
84 | * @return The number of rounds won
85 | */
86 | public int getRoundsWon() {
87 | return this.roundsWon;
88 | }
89 |
90 | /**
91 | * Returns the percentage of rounds the player has won on this map
92 | *
93 | * @return The percentage of rounds won
94 | */
95 | public float getRoundsWonPercentage() {
96 | return (this.roundsPlayed > 0) ? (this.roundsWon / this.roundsPlayed) : 0;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/AbtractL4DWeapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import com.github.koraktor.steamcondenser.community.GameWeapon;
11 | import com.github.koraktor.steamcondenser.community.XMLData;
12 |
13 | /**
14 | * This abstract class is a base class for weapons in Left4Dead and Left4Dead 2
15 | * as the weapon stats for both games are very similar
16 | *
17 | * @author Sebastian Staudt
18 | */
19 | public abstract class AbtractL4DWeapon extends GameWeapon {
20 |
21 | protected float accuracy;
22 |
23 | protected float headshotPercentage;
24 |
25 | protected float killPercentage;
26 |
27 | protected String name;
28 |
29 | protected int shots;
30 |
31 | /**
32 | * Creates a new instance of weapon from the given XML data and parses
33 | * common data for both, L4DWeapon and L4D2Weapon
34 | *
35 | * @param weaponData The XML data for this weapon
36 | */
37 | public AbtractL4DWeapon(XMLData weaponData) {
38 | super(weaponData);
39 |
40 | this.accuracy = Float.parseFloat(weaponData
41 | .getString("accuracy").replace("%", "")) * 0.01f;
42 | this.headshotPercentage = Float.parseFloat(weaponData
43 | .getString("headshots").replace("%", "")) * 0.01f;
44 | this.name = weaponData.getName();
45 | this.shots = weaponData.getInteger("shots");
46 | }
47 |
48 | /**
49 | * Returns the overall accuracy of the player with this weapon
50 | *
51 | * @return The accuracy of the player with this weapon
52 | */
53 | public float getAccuracy() {
54 | return this.accuracy;
55 | }
56 |
57 | /**
58 | * Returns the percentage of kills with this weapon that have been
59 | * headshots
60 | *
61 | * @return The percentage of headshots with this weapon
62 | */
63 | public float getHeadshotPercentage() {
64 | return this.headshotPercentage;
65 | }
66 |
67 | /**
68 | * Returns the percentage of overall kills of the player that have been
69 | * achieved with this weapon
70 | *
71 | * @return The percentage of kills with this weapon
72 | */
73 | public float getKillPercentage() {
74 | return this.killPercentage;
75 | }
76 |
77 | /**
78 | * Returns the name of the weapon
79 | *
80 | * @return The name of the weapon
81 | */
82 | public String getName() {
83 | return this.name;
84 | }
85 |
86 | /**
87 | * Returns the number of shots the player has fired with this weapon
88 | *
89 | * @return The number of shots with this weapon
90 | */
91 | public int getShots() {
92 | return this.shots;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/dods/DoDSStats.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.dods;
9 |
10 | import java.util.HashMap;
11 |
12 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
13 | import com.github.koraktor.steamcondenser.community.GameStats;
14 | import com.github.koraktor.steamcondenser.community.XMLData;
15 |
16 | /**
17 | * The is class represents the game statistics for a single user in Day of
18 | * Defeat: Source
19 | *
20 | * @author Sebastian Staudt
21 | */
22 | public class DoDSStats extends GameStats {
23 |
24 | private HashMap classStats;
25 | private HashMap weaponStats;
26 |
27 | /**
28 | * Creates a DoDSStats instance by calling the super
29 | * constructor with the game name "DoD:S"
30 | *
31 | * @param steamId The custom URL or 64bit Steam ID of the user
32 | * @throws SteamCondenserException if an error occurs while fetching the
33 | * stats data
34 | */
35 | public DoDSStats(Object steamId) throws SteamCondenserException {
36 | super(steamId, "dod:s");
37 | }
38 |
39 | /**
40 | * Returns a map of DoDSClass for this user containing all
41 | * DoD:S classes.
42 | *
43 | * If the classes haven't been parsed already, parsing is done now.
44 | *
45 | * @return The class statistics for this user
46 | */
47 | public HashMap getClassStats() {
48 | if(!this.isPublic()) {
49 | return null;
50 | }
51 |
52 | if(this.classStats == null) {
53 | this.classStats = new HashMap<>();
54 | for(XMLData classData : this.xmlData.getElements("classes", "class")) {
55 | this.classStats.put(classData.getAttribute("key"),
56 | new DoDSClass(classData));
57 | }
58 | }
59 |
60 | return this.classStats;
61 | }
62 |
63 | /**
64 | * Returns a map of DoDSWeapon for this user containing all
65 | * DoD:S weapons.
66 | *
67 | * If the weapons haven't been parsed already, parsing is done now.
68 | *
69 | * @return The weapon statistics for this user
70 | */
71 | public HashMap getWeaponStats() {
72 | if(!this.isPublic()) {
73 | return null;
74 | }
75 |
76 | if(this.weaponStats == null) {
77 | this.weaponStats = new HashMap<>();
78 | for(XMLData weaponData : this.xmlData.getChildren("weapons")) {
79 | this.weaponStats.put(weaponData.getAttribute("key"),
80 | new DoDSWeapon(weaponData));
81 | }
82 | }
83 |
84 | return this.weaponStats;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/SteamPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import com.github.koraktor.steamcondenser.PacketBuffer;
11 |
12 | /**
13 | * This module implements the basic functionality used by most of the packets
14 | * used in communication with master, Source or GoldSrc servers.
15 | *
16 | * @author Sebastian Staudt
17 | * @see SteamPacketFactory
18 | */
19 | abstract public class SteamPacket {
20 |
21 | public static final byte A2S_INFO_HEADER = 0x54;
22 | public static final byte S2A_INFO2_HEADER = 0x49;
23 | public static final byte S2A_INFO_DETAILED_HEADER = 0x6D;
24 | public static final byte A2S_PLAYER_HEADER = 0x55;
25 | public static final byte S2A_PLAYER_HEADER = 0x44;
26 | public static final byte A2S_RULES_HEADER = 0x56;
27 | public static final byte S2A_RULES_HEADER = 0x45;
28 | public static final byte A2S_SERVERQUERY_GETCHALLENGE_HEADER = 0x57;
29 | public static final byte S2C_CHALLENGE_HEADER = 0x41;
30 | public static final byte A2M_GET_SERVERS_BATCH2_HEADER = 0x31;
31 | public static final byte M2A_SERVER_BATCH_HEADER = 0x66;
32 | public static final byte RCON_GOLDSRC_CHALLENGE_HEADER = 0x63;
33 | public static final byte RCON_GOLDSRC_NO_CHALLENGE_HEADER = 0x39;
34 | public static final byte RCON_GOLDSRC_RESPONSE_HEADER = 0x6c;
35 |
36 | /**
37 | * This variable stores the content of the package
38 | */
39 | protected PacketBuffer contentData;
40 |
41 | /**
42 | * This byte stores the type of the packet
43 | */
44 | protected byte headerData;
45 |
46 |
47 | /**
48 | * Creates a new packet object based on the given data
49 | *
50 | * @param headerData The packet header
51 | */
52 | protected SteamPacket(byte headerData) {
53 | this(headerData, new byte[0]);
54 | }
55 |
56 | /**
57 | * Creates a new packet object based on the given data
58 | *
59 | * @param headerData The packet header
60 | * @param contentBytes The raw data of the packet
61 | */
62 | protected SteamPacket(byte headerData, byte[] contentBytes) {
63 | this.contentData = new PacketBuffer(contentBytes);
64 | this.headerData = headerData;
65 | }
66 |
67 | /**
68 | * Returns the raw data representing this packet
69 | *
70 | * @return A byte array containing the raw data of this request packet
71 | */
72 | public byte[] getBytes() {
73 | byte[] bytes = new byte[this.contentData.getLength() + 5];
74 | bytes[0] = (byte) 0xFF;
75 | bytes[1] = (byte) 0xFF;
76 | bytes[2] = (byte) 0xFF;
77 | bytes[3] = (byte) 0xFF;
78 | bytes[4] = this.headerData;
79 | System.arraycopy(this.contentData.array(), 0, bytes, 5, bytes.length - 5);
80 | return bytes;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/L4DMap.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * This class holds statistical information about a map played by a player in
14 | * Survival mode of Left4Dead
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class L4DMap {
19 |
20 | public static int GOLD = 1;
21 | public static int SILVER = 2;
22 | public static int BRONZE = 3;
23 | public static int NONE = 0;
24 |
25 | protected float bestTime;
26 |
27 | protected String id;
28 |
29 | protected int medal;
30 |
31 | protected String name;
32 |
33 | private int timesPlayed;
34 |
35 | public L4DMap() {}
36 |
37 | /**
38 | * Creates a new instance of a Left4Dead Survival map based on the given
39 | * XML data
40 | *
41 | * @param mapData The XML data for this map
42 | */
43 | public L4DMap(XMLData mapData) {
44 | this.bestTime = mapData.getFloat("besttimeseconds");
45 | this.id = mapData.getName();
46 | this.name = mapData.getString("name");
47 | this.timesPlayed = mapData.getInteger("timesplayed");
48 |
49 | String medal = mapData.getString("medal");
50 | if(medal.equals("gold")) {
51 | this.medal = GOLD;
52 | } else if(medal.equals("silver")) {
53 | this.medal = SILVER;
54 | } else if(medal.equals("bronze")) {
55 | this.medal = BRONZE;
56 | } else {
57 | this.medal = NONE;
58 | }
59 | }
60 |
61 | /**
62 | * Returns the best survival time of this player on this map
63 | *
64 | * @return The best survival time of this player on this map
65 | */
66 | public float getBestTime() {
67 | return this.bestTime;
68 | }
69 |
70 | /**
71 | * Returns the ID of this map
72 | *
73 | * @return The ID of this map
74 | */
75 | public String getId() {
76 | return this.id;
77 | }
78 |
79 | /**
80 | * Returns the highest medal this player has won on this map
81 | *
82 | * @return The highest medal won by this player on this map
83 | */
84 | public int getMedal() {
85 | return this.medal;
86 | }
87 |
88 | /**
89 | * Returns the name of the map
90 | *
91 | * @return The name of the map
92 | */
93 | public String getName() {
94 | return this.name;
95 | }
96 |
97 | /**
98 | * Returns the number of times this map has been played by this player
99 | *
100 | * @return The number of times this map has been played
101 | */
102 | public int getTimesPlayed() {
103 | return this.timesPlayed;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/S2A_INFO_DETAILED_Packet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import java.util.HashMap;
11 |
12 | /**
13 | * This class represents a S2A_INFO_DETAILED response packet sent by a GoldSrc
14 | * server
15 | *
16 | * @author Sebastian Staudt
17 | * @deprecated Only outdated GoldSrc servers (before 10/24/2008) use this
18 | * format. Newer ones use the same format as Source servers now
19 | * (see {@link S2A_INFO2_Packet}).
20 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateServerInfo
21 | */
22 | public class S2A_INFO_DETAILED_Packet extends S2A_INFO_BasePacket {
23 |
24 | /**
25 | * Creates a new S2A_INFO_DETAILED response object based on the given data
26 | *
27 | * @param dataBytes The raw packet data replied from the server
28 | */
29 | public S2A_INFO_DETAILED_Packet(byte[] dataBytes) {
30 | super(SteamPacket.S2A_INFO_DETAILED_HEADER, dataBytes);
31 |
32 | this.info.put("serverIp", this.contentData.getString());
33 | this.info.put("serverName", this.contentData.getString());
34 | this.info.put("mapName", this.contentData.getString());
35 | this.info.put("gameDir", this.contentData.getString());
36 | this.info.put("gameDescription", this.contentData.getString());
37 | this.info.put("numberOfPlayers", this.contentData.getByte());
38 | this.info.put("maxPlayers", this.contentData.getByte());
39 | this.info.put("networkVersion", this.contentData.getByte());
40 | this.info.put("dedicated", this.contentData.getByte());
41 | this.info.put("operatingSystem", this.contentData.getByte());
42 | this.info.put("passwordProtected", this.contentData.getByte() == 1);
43 | boolean isMod = this.contentData.getByte() == 1;
44 | this.info.put("isMod", isMod);
45 |
46 | if(isMod) {
47 | HashMap modInfo = new HashMap<>(6);
48 | modInfo.put("urlInfo", this.contentData.getString());
49 | modInfo.put("urlDl", this.contentData.getString());
50 | this.contentData.getByte();
51 | if(this.contentData.remaining() == 12) {
52 | modInfo.put("modVersion", Integer.reverseBytes(this.contentData.getInt()));
53 | modInfo.put("modSize", Integer.reverseBytes(this.contentData.getInt()));
54 | modInfo.put("svOnly", this.contentData.getByte() == 1);
55 | modInfo.put("clDll", this.contentData.getByte() == 1);
56 | this.info.put("secure", this.contentData.getByte() == 1);
57 | this.info.put("numberOfBots", this.contentData.getByte());
58 | }
59 | this.info.put("modInfo", modInfo);
60 | } else {
61 | this.info.put("secure", this.contentData.getByte() == 1);
62 | this.info.put("numberOfBots", this.contentData.getByte());
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/A2M_GET_SERVERS_BATCH2_Packet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | import com.github.koraktor.steamcondenser.servers.MasterServer;
11 |
12 | /**
13 | * This packet class represents a A2M_GET_SERVERS_BATCH2 request sent to a
14 | * master server
15 | *
16 | * It is used to receive a list of game servers matching the specified filters.
17 | *
18 | * Filtering:
19 | *
20 | * Instead of filtering the results sent by the master server locally, you
21 | * should at least use the following filters to narrow down the results sent by
22 | * the master server. Receiving all servers from the master server is taking
23 | * quite some time.
24 | *
25 | * Available filters:
26 | *
27 | * - \type\d: Request only dedicated servers
28 | * - \secure\1: Request only secure servers
29 | * - \gamedir\[mod]: Request only servers of a specific mod
30 | * - \map\[mapname]: Request only servers running a specific map
31 | * - \linux\1: Request only linux servers
32 | * - \emtpy\1: Request only non-empty servers
33 | * - \full\1: Request only servers not full
34 | * - \proxy\1: Request only spectator proxy servers
35 | *
36 | *
37 | * @author Sebastian Staudt
38 | * @see MasterServer#getServers(byte, String)
39 | */
40 | public class A2M_GET_SERVERS_BATCH2_Packet extends SteamPacket {
41 |
42 | private String filter;
43 | private byte regionCode;
44 | private String startIp;
45 |
46 | /**
47 | * Creates a new A2M_GET_SERVERS_BATCH2 request object, filtering by the
48 | * given paramters
49 | *
50 | * @param regionCode The region code to filter servers by region.
51 | * @param startIp This should be the last IP received from the master
52 | * server or 0.0.0.0
53 | * @param filter The filters to apply in the form ("\filtername\value...")
54 | */
55 | public A2M_GET_SERVERS_BATCH2_Packet(byte regionCode, String startIp, String filter) {
56 | super(SteamPacket.A2M_GET_SERVERS_BATCH2_HEADER);
57 |
58 | this.filter = filter;
59 | this.regionCode = regionCode;
60 | this.startIp = startIp;
61 | }
62 |
63 | /**
64 | * Returns the raw data representing this packet
65 | *
66 | * @return A byte array containing the raw data of this request packet
67 | */
68 | @Override
69 | public byte[] getBytes() {
70 | byte[] bytes, filterBytes, startIpBytes;
71 |
72 | filterBytes = (this.filter + "\0").getBytes();
73 | startIpBytes = (this.startIp + "\0").getBytes();
74 | bytes = new byte[2 + startIpBytes.length + filterBytes.length];
75 |
76 | bytes[0] = this.headerData;
77 | bytes[1] = this.regionCode;
78 | System.arraycopy(startIpBytes, 0, bytes, 2, startIpBytes.length);
79 | System.arraycopy(filterBytes, 0, bytes, startIpBytes.length + 2, filterBytes.length);
80 |
81 | return bytes;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/rcon/RCONPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets.rcon;
9 |
10 | import com.github.koraktor.steamcondenser.Helper;
11 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
12 |
13 | /**
14 | * This module is included by all classes representing a packet used by
15 | * Source's RCON protocol
16 | *
17 | * It provides a basic implementation for initializing and serializing such a
18 | * packet.
19 | *
20 | * @author Sebastian Staudt
21 | * @see RCONPacketFactory
22 | */
23 | abstract public class RCONPacket extends SteamPacket {
24 |
25 | /**
26 | * Header for authentication requests
27 | */
28 | public static final byte SERVERDATA_AUTH = 3;
29 |
30 | /**
31 | * Header for replies to authentication attempts
32 | */
33 | public static final byte SERVERDATA_AUTH_RESPONSE = 2;
34 |
35 | /**
36 | * Header for command execution requests
37 | */
38 | public static final byte SERVERDATA_EXECCOMMAND = 2;
39 |
40 | /**
41 | * Header for packets with the output of a command execution
42 | */
43 | public static final byte SERVERDATA_RESPONSE_VALUE = 0;
44 |
45 | /**
46 | * The packet header specifying the packet type
47 | */
48 | protected int header;
49 |
50 | /**
51 | * The request ID used to identify the RCON communication
52 | */
53 | protected int requestId;
54 |
55 | /**
56 | * Creates a new RCON packet object with the given request ID, type and
57 | * content data
58 | *
59 | * @param requestId The request ID for the current RCON communication
60 | * @param rconHeader The header for the packet type
61 | * @param rconData The raw packet data
62 | */
63 | protected RCONPacket(int requestId, int rconHeader, String rconData) {
64 | super((byte) 0, (rconData + "\0\0").getBytes());
65 |
66 | this.header = rconHeader;
67 | this.requestId = requestId;
68 | }
69 |
70 | /**
71 | * Returns the raw data representing this packet
72 | *
73 | * @return A byte array containing the raw data of this RCON packet
74 | */
75 | public byte[] getBytes() {
76 | byte[] bytes = new byte[this.contentData.getLength() + 12];
77 |
78 | System.arraycopy(Helper.byteArrayFromInteger(Integer.reverseBytes(bytes.length - 4)), 0, bytes, 0, 4);
79 | System.arraycopy(Helper.byteArrayFromInteger(Integer.reverseBytes(this.requestId)), 0, bytes, 4, 4);
80 | System.arraycopy(Helper.byteArrayFromInteger(Integer.reverseBytes(this.header)), 0, bytes, 8, 4);
81 | System.arraycopy(this.contentData.array(), 0, bytes, 12, bytes.length - 12);
82 |
83 | return bytes;
84 | }
85 |
86 | /**
87 | * Returns the request ID used to identify the RCON communication
88 | *
89 | * @return The request ID used to identify the RCON communication
90 | */
91 | public int getRequestId() {
92 | return this.requestId;
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/sockets/MasterServerSocketTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.net.InetAddress;
11 | import java.nio.ByteBuffer;
12 |
13 | import org.junit.Before;
14 | import org.junit.Rule;
15 | import org.junit.Test;
16 | import org.junit.rules.ExpectedException;
17 | import org.junit.runner.RunWith;
18 |
19 | import org.mockito.invocation.InvocationOnMock;
20 | import org.mockito.stubbing.Answer;
21 | import org.powermock.core.classloader.annotations.PrepareForTest;
22 | import org.powermock.modules.junit4.PowerMockRunner;
23 |
24 | import com.github.koraktor.steamcondenser.exceptions.PacketFormatException;
25 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
26 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacketFactory;
27 |
28 | import static org.junit.Assert.assertEquals;
29 | import static org.mockito.Mockito.doReturn;
30 | import static org.mockito.Mockito.mock;
31 | import static org.mockito.Mockito.spy;
32 | import static org.powermock.api.mockito.PowerMockito.doAnswer;
33 | import static org.powermock.api.mockito.PowerMockito.mockStatic;
34 | import static org.powermock.api.mockito.PowerMockito.when;
35 |
36 | /**
37 | * @author Sebastian Staudt
38 | */
39 | @RunWith(PowerMockRunner.class)
40 | public class MasterServerSocketTest {
41 |
42 | @Rule
43 | public ExpectedException exception = ExpectedException.none();
44 |
45 | private MasterServerSocket socket;
46 |
47 | @Before
48 | public void setup() throws Exception {
49 | this.socket = new MasterServerSocket(InetAddress.getLocalHost(), 27015);
50 | }
51 |
52 | @Test
53 | public void testIncorrectPacket() throws Exception {
54 | this.exception.expect(PacketFormatException.class);
55 | this.exception.expectMessage("Master query response has wrong packet header.");
56 |
57 | MasterServerSocket socket = spy(this.socket);
58 | doReturn(1).when(socket).receivePacket(1500);
59 | socket.buffer = mock(ByteBuffer.class);
60 | when(socket.buffer.getInt()).thenReturn(1);
61 |
62 | socket.getReply();
63 | }
64 |
65 | @Test
66 | @PrepareForTest(SteamPacketFactory.class)
67 | public void testCorrectPacket() throws Exception {
68 | SteamPacket packet = mock(SteamPacket.class);
69 | mockStatic(SteamPacketFactory.class);
70 | when(SteamPacketFactory.getPacketFromData("test".getBytes())).thenReturn(packet);
71 |
72 | MasterServerSocket socket = spy(this.socket);
73 | doAnswer(new Answer() {
74 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
75 | MasterServerSocket socket = (MasterServerSocket) invocationOnMock.getMock();
76 | socket.buffer = ByteBuffer.wrap(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 't', 'e', 's', 't' });
77 | return 8;
78 | }
79 | }).when(socket).receivePacket(1500);
80 |
81 | assertEquals(packet, socket.getReply());
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/css/CSSWeapon.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.css;
9 |
10 | import com.github.koraktor.steamcondenser.community.XMLData;
11 |
12 | /**
13 | * Represents the stats for a Counter-Strike: Source weapon for a specific user
14 | *
15 | * @author Sebastian Staudt
16 | */
17 | public class CSSWeapon {
18 |
19 | private boolean favorite;
20 |
21 | private int hits;
22 |
23 | private int kills;
24 |
25 | private String name;
26 |
27 | private int shots;
28 |
29 | /**
30 | * Creates a new instance of a Counter-Strike: Source weapon based on the
31 | * given XML data
32 | *
33 | * @param weaponName The name of the weapon
34 | * @param weaponsData The XML data of all weapons
35 | */
36 | public CSSWeapon(String weaponName, XMLData weaponsData) {
37 | this.name = weaponName;
38 |
39 | this.favorite = weaponsData.getString("favorite").equals(this.name);
40 | this.kills = weaponsData.getInteger(this.name + "_kills");
41 |
42 | if(!this.name.equals("grenade") && !this.name.equals("knife")) {
43 | this.hits = weaponsData.getInteger(this.name + "_hits");
44 | this.shots = weaponsData.getInteger(this.name + "_shots");
45 | }
46 | }
47 |
48 | /**
49 | * Returns whether this weapon is the favorite weapon of this player
50 | *
51 | * @return true if this is the favorite weapon
52 | */
53 | public boolean isFavorite() {
54 | return this.favorite;
55 | }
56 |
57 | /**
58 | * Returns the accuracy of this player with this weapon
59 | *
60 | * @return The accuracy with this weapon
61 | */
62 | public float getAccuracy() {
63 | return (this.shots > 0) ? this.hits / this.shots : 0;
64 | }
65 |
66 | /**
67 | * Returns the number of hits achieved with this weapon
68 | *
69 | * @return The number of hits achieved
70 | */
71 | public int getHits() {
72 | return this.hits;
73 | }
74 |
75 | /**
76 | * Returns the number of kills achieved with this weapon
77 | *
78 | * @return The number of kills achieved
79 | */
80 | public int getKills() {
81 | return this.kills;
82 | }
83 |
84 | /**
85 | * Returns the kill-shot-ratio of this player with this weapon
86 | *
87 | * @return The kill-shot-ratio
88 | */
89 | public float getKsRatio() {
90 | return (this.shots > 0) ? this.kills / this.shots : 0;
91 | }
92 |
93 | /**
94 | * Returns the name of this weapon
95 | *
96 | * @return The name of this weapon
97 | */
98 | public String getName() {
99 | return this.name;
100 | }
101 |
102 | /**
103 | * Returns the number of shots fired with this weapon
104 | *
105 | * @return The number of shots fired
106 | */
107 | public int getShots() {
108 | return this.shots;
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/L4DStats.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
14 | import com.github.koraktor.steamcondenser.community.GameWeapon;
15 | import com.github.koraktor.steamcondenser.community.XMLData;
16 |
17 | /**
18 | * This class represents the game statistics for a single user in Left4Dead
19 | *
20 | * @author Sebastian Staudt
21 | */
22 | public class L4DStats extends AbstractL4DStats {
23 |
24 | /**
25 | * Creates a L4DStats object by calling the super constructor
26 | * with the game name "l4d"
27 | *
28 | * @param steamId The custom URL or 64bit Steam ID of the user
29 | * @throws SteamCondenserException if an error occurs while fetching the
30 | * stats data
31 | */
32 | public L4DStats(Object steamId) throws SteamCondenserException {
33 | super(steamId, "l4d");
34 | }
35 |
36 | /**
37 | * Returns a map of Survival statistics for this user like revived
38 | * teammates
39 | *
40 | * If the Survival statistics haven't been parsed already, parsing is done
41 | * now.
42 | *
43 | * @return The stats for the Survival mode
44 | */
45 | public Map getSurvivalStats()
46 | throws SteamCondenserException {
47 | if(!this.isPublic()) {
48 | return null;
49 | }
50 |
51 | if(this.survivalStats == null) {
52 | super.getSurvivalStats();
53 | HashMap mapsHash = new HashMap<>();
54 | for(XMLData mapData : this.xmlData.getElements("stats", "survival", "maps")) {
55 | mapsHash.put(mapData.getName(), new L4DMap(mapData));
56 | }
57 | this.survivalStats.put("maps", mapsHash);
58 | }
59 |
60 | return this.survivalStats;
61 | }
62 |
63 | /**
64 | * Returns a map of L4DWeapon for this user containing all
65 | * Left4Dead weapons
66 | *
67 | * If the weapons haven't been parsed already, parsing is done now.
68 | *
69 | * @return The weapon statistics
70 | */
71 | public Map getWeaponStats() {
72 | if(!this.isPublic()) {
73 | return null;
74 | }
75 |
76 | if(this.weaponStats == null) {
77 | this.weaponStats = new HashMap<>();
78 | for(XMLData weaponData : this.xmlData.getChildren("stats", "weapons")) {
79 | String weaponName = weaponData.getName();
80 | GameWeapon weapon;
81 | if(!weaponName.equals("molotov") && !weaponName.equals("pipes")) {
82 | weapon = new L4DWeapon(weaponData);
83 | }
84 | else {
85 | weapon = new L4DExplosive(weaponData);
86 | }
87 | this.weaponStats.put(weaponName, weapon);
88 | }
89 | }
90 |
91 | return this.weaponStats;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/sockets/QuerySocket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.io.IOException;
11 | import java.net.InetAddress;
12 | import java.nio.ByteBuffer;
13 | import java.nio.channels.DatagramChannel;
14 | import java.util.concurrent.TimeoutException;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
19 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
20 |
21 | /**
22 | * This class implements basic functionality for UDP based sockets
23 | *
24 | * @author Sebastian Staudt
25 | */
26 | public abstract class QuerySocket extends SteamSocket {
27 |
28 | protected static final Logger LOG = LoggerFactory.getLogger(QuerySocket.class);
29 |
30 | /**
31 | * Creates a new socket to communicate with the server on the given IP
32 | * address and port
33 | *
34 | * @param ipAddress Either the IP address or the DNS name of the server
35 | * @param portNumber The port the server is listening on
36 | * @throws SteamCondenserException if the socket cannot be opened
37 | */
38 | protected QuerySocket(InetAddress ipAddress, int portNumber)
39 | throws SteamCondenserException {
40 | super(ipAddress, portNumber);
41 |
42 | try {
43 | this.channel = DatagramChannel.open();
44 | this.channel.configureBlocking(false);
45 | ((DatagramChannel) this.channel).connect(this.remoteSocket);
46 | } catch(IOException e) {
47 | throw new SteamCondenserException(e.getMessage(), e);
48 | }
49 | }
50 |
51 | /**
52 | * Returns whether a packet in the buffer is split
53 | *
54 | * @return true if the packet is split
55 | */
56 | protected boolean packetIsSplit() {
57 | return (Integer.reverseBytes(this.buffer.getInt()) == 0xFFFFFFFE);
58 | }
59 |
60 | /**
61 | * Reads an UDP packet into the buffer
62 | *
63 | * @return The number of bytes received
64 | * @throws SteamCondenserException if an error occurs while reading from
65 | * the socket
66 | * @throws TimeoutException if no UDP packet was received
67 | */
68 | protected int receivePacket()
69 | throws SteamCondenserException, TimeoutException {
70 | return this.receivePacket(0);
71 | }
72 |
73 | /**
74 | * Sends the given packet to the server
75 | *
76 | * @param dataPacket The packet to send to the server
77 | * @throws SteamCondenserException if an error occurs while writing to the
78 | * socket
79 | */
80 | public void send(SteamPacket dataPacket)
81 | throws SteamCondenserException {
82 | LOG.info("Sending data packet of type \"" + dataPacket.getClass().getSimpleName() + "\"");
83 |
84 | try {
85 | this.buffer = ByteBuffer.wrap(dataPacket.getBytes());
86 | ((DatagramChannel) this.channel).send(this.buffer, this.remoteSocket);
87 | this.buffer.flip();
88 | } catch(IOException e) {
89 | throw new SteamCondenserException(e.getMessage(), e);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/packets/S2A_INFO2_Packet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.packets;
9 |
10 | /**
11 | * This class represents a S2A_INFO_DETAILED response packet sent by a Source
12 | * or GoldSrc server
13 | *
14 | * Out-of-date (before 10/24/2008) GoldSrc servers use an older format (see
15 | * {@link S2A_INFO_DETAILED_Packet}).
16 | *
17 | * @author Sebastian Staudt
18 | * @see com.github.koraktor.steamcondenser.servers.GameServer#updateServerInfo
19 | */
20 | public class S2A_INFO2_Packet extends S2A_INFO_BasePacket {
21 |
22 | private static byte EDF_GAME_ID = (byte) 0x01;
23 | private static byte EDF_GAME_PORT = (byte) 0x80;
24 | private static byte EDF_SERVER_ID = (byte) 0x10;
25 | private static byte EDF_SERVER_TAGS = (byte) 0x20;
26 | private static byte EDF_SOURCE_TV = (byte) 0x40;
27 |
28 | /**
29 | * Creates a new S2A_INFO2 response object based on the given data
30 | *
31 | * @param dataBytes The raw packet data replied from the server
32 | */
33 | public S2A_INFO2_Packet(byte[] dataBytes) {
34 | super(SteamPacket.S2A_INFO2_HEADER, dataBytes);
35 |
36 | this.info.put("networkVersion", this.contentData.getByte());
37 | this.info.put("serverName", this.contentData.getString());
38 | this.info.put("mapName", this.contentData.getString());
39 | this.info.put("gameDir", this.contentData.getString());
40 | this.info.put("gameDescription", this.contentData.getString());
41 | this.info.put("appId", Short.reverseBytes(this.contentData.getShort()));
42 | this.info.put("numberOfPlayers", this.contentData.getByte());
43 | this.info.put("maxPlayers", this.contentData.getByte());
44 | this.info.put("numberOfBots", this.contentData.getByte());
45 | this.info.put("dedicated", this.contentData.getByte());
46 | this.info.put("operatingSystem", this.contentData.getByte());
47 | this.info.put("passwordProtected", this.contentData.getByte() == 1);
48 | this.info.put("secure", this.contentData.getByte() == 1);
49 | this.info.put("gameVersion", this.contentData.getString());
50 |
51 | if(this.contentData.remaining() > 0) {
52 | byte extraDataFlag = this.contentData.getByte();
53 |
54 | if ((extraDataFlag & EDF_GAME_PORT) != 0) {
55 | this.info.put("serverPort", Short.reverseBytes(this.contentData.getShort()));
56 | }
57 |
58 | if ((extraDataFlag & EDF_SERVER_ID) != 0) {
59 | this.info.put("serverId", Long.reverseBytes((this.contentData.getInt() << 32) | this.contentData.getInt()));
60 | }
61 |
62 | if ((extraDataFlag & EDF_SOURCE_TV) != 0) {
63 | this.info.put("tvPort", Short.reverseBytes(this.contentData.getShort()));
64 | this.info.put("tvName", this.contentData.getString());
65 | }
66 |
67 | if ((extraDataFlag & EDF_SERVER_TAGS) != 0) {
68 | this.info.put("serverTags", this.contentData.getString());
69 | }
70 |
71 | if ((extraDataFlag & EDF_GAME_ID) != 0) {
72 | this.info.put("gameId", Long.reverseBytes((this.contentData.getInt() << 32) | this.contentData.getInt()));
73 | }
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/sockets/QuerySocketTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2020, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.net.InetAddress;
11 | import java.net.InetSocketAddress;
12 | import java.net.UnknownHostException;
13 | import java.nio.ByteBuffer;
14 | import java.nio.channels.DatagramChannel;
15 | import java.util.Arrays;
16 |
17 | import org.junit.Before;
18 | import org.junit.Test;
19 |
20 | import org.mockito.ArgumentMatcher;
21 |
22 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
23 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
24 |
25 | import static org.junit.Assert.assertEquals;
26 | import static org.mockito.ArgumentMatchers.argThat;
27 | import static org.mockito.Mockito.doReturn;
28 | import static org.mockito.Mockito.mock;
29 | import static org.mockito.Mockito.spy;
30 | import static org.mockito.Mockito.verify;
31 | import static org.mockito.Mockito.when;
32 |
33 | /**
34 | * @author Sebastian Staudt
35 | */
36 | public class QuerySocketTest {
37 |
38 | private DatagramChannel channel;
39 |
40 | private QuerySocket socket;
41 |
42 | @Before
43 | public void setup() throws Exception {
44 | this.socket = new GenericQuerySocket();
45 |
46 | this.channel = mock(DatagramChannel.class);
47 | this.socket.channel = this.channel;
48 | }
49 |
50 | @Test
51 | public void testPacketIsSplit() {
52 | this.socket.buffer = mock(ByteBuffer.class);
53 | when(this.socket.buffer.getInt()).thenReturn(0xFFFFFFFF).thenReturn(0xFEFFFFFF);
54 |
55 | assertEquals(false, this.socket.packetIsSplit());
56 | assertEquals(true, this.socket.packetIsSplit());
57 | }
58 |
59 | @Test
60 | public void testReceivePacket() throws Exception {
61 | QuerySocket socket = spy(this.socket);
62 | doReturn(0).when(socket).receivePacket(0);
63 |
64 | socket.receivePacket();
65 |
66 | verify(socket).receivePacket(0);
67 | }
68 |
69 | @Test
70 | public void testSend() throws Exception {
71 | SteamPacket packet = mock(SteamPacket.class);
72 | when(packet.getBytes()).thenReturn(new byte[] { 0x1, 0x2, 0x3, 0x4 } );
73 |
74 | this.socket.send(packet);
75 |
76 | ArgumentMatcher bufferMatcher = new ArgumentMatcher() {
77 | public boolean matches(ByteBuffer buffer) {
78 | return Arrays.equals(buffer.array(), new byte[] { 0x1, 0x2, 0x3, 0x4 });
79 | }
80 | };
81 |
82 | ArgumentMatcher socketMatcher = new ArgumentMatcher() {
83 | public boolean matches(InetSocketAddress socketAddress) {
84 | return socketAddress.getAddress().isLoopbackAddress() &&
85 | socketAddress.getPort() == 27015;
86 | }
87 | };
88 |
89 | verify(this.channel).send(argThat(bufferMatcher), argThat(socketMatcher));
90 | }
91 |
92 | class GenericQuerySocket extends QuerySocket {
93 |
94 | public GenericQuerySocket() throws SteamCondenserException, UnknownHostException {
95 | super(InetAddress.getByName("127.0.0.1"), 27015);
96 | }
97 |
98 | public SteamPacket getReply() {
99 | return null;
100 | }
101 |
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/GoldSrcServerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers;
9 |
10 | import java.net.InetAddress;
11 |
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 |
16 | import org.powermock.core.classloader.annotations.PrepareForTest;
17 | import org.powermock.modules.junit4.PowerMockRunner;
18 |
19 | import com.github.koraktor.steamcondenser.servers.sockets.GoldSrcSocket;
20 | import com.github.koraktor.steamcondenser.exceptions.RCONNoAuthException;
21 |
22 | import static org.hamcrest.core.Is.is;
23 | import static org.hamcrest.core.IsEqual.equalTo;
24 | import static org.hamcrest.core.IsNull.nullValue;
25 | import static org.junit.Assert.assertThat;
26 | import static org.junit.Assert.assertTrue;
27 | import static org.mockito.Mockito.mock;
28 | import static org.mockito.Mockito.when;
29 | import static org.powermock.api.mockito.PowerMockito.spy;
30 | import static org.powermock.api.mockito.PowerMockito.whenNew;
31 |
32 | /**
33 | *
34 | *
35 | * @author Sebastian Staudt
36 | */
37 | @RunWith(PowerMockRunner.class)
38 | @PrepareForTest(GoldSrcServer.class)
39 | public class GoldSrcServerTest {
40 |
41 | private static InetAddress LOCALHOST;
42 |
43 | private GoldSrcServer server;
44 |
45 | private GoldSrcSocket socket;
46 |
47 | @Before
48 | public void setup() throws Exception {
49 | LOCALHOST = InetAddress.getByAddress(new byte[] { 0x7f, 0x0, 0x0, 0x1 });
50 | this.server = spy(new GoldSrcServer(LOCALHOST, 27015));
51 | this.socket = mock(GoldSrcSocket.class);
52 | this.server.socket = this.socket;
53 | }
54 |
55 | @Test
56 | public void testGetMaster() throws Exception {
57 | MasterServer master = mock(MasterServer.class);
58 | whenNew(MasterServer.class).withArguments(MasterServer.GOLDSRC_MASTER_SERVER).thenReturn(master);
59 |
60 | assertThat(GoldSrcServer.getMaster(), is(equalTo(master)));
61 | }
62 |
63 | @Test
64 | public void testInitSocket() throws Exception {
65 | GoldSrcSocket socket = mock(GoldSrcSocket.class);
66 | whenNew(GoldSrcSocket.class).withArguments(LOCALHOST, 27015, false).thenReturn(socket);
67 |
68 | this.server.initSocket();
69 |
70 | assertThat((GoldSrcSocket) this.server.socket, is(equalTo(socket)));
71 | }
72 |
73 | @Test
74 | public void testRconAuthFailed() throws Exception {
75 | when(this.socket.rconExec("password", "")).
76 | thenThrow(new RCONNoAuthException());
77 |
78 | assertThat(this.server.rconAuth("password"), is(false));
79 | assertThat(this.server.rconPassword, is(nullValue()));
80 | }
81 |
82 | @Test
83 | public void testRconAuthSuccessful() throws Exception {
84 | when(this.socket.rconExec("password", "")).thenReturn("");
85 |
86 | assertTrue(this.server.rconAuth("password"));
87 | assertThat(this.server.rconPassword, is(equalTo("password")));
88 | }
89 |
90 | @Test
91 | public void testRconExec() throws Exception {
92 | this.server.rconAuthenticated = true;
93 | when(this.socket.rconExec("password", "command")).thenReturn("test");
94 |
95 | this.server.rconPassword = "password";
96 |
97 | assertThat(this.server.rconExec("command"), is(equalTo("test")));
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/PacketBuffer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser;
9 |
10 | import java.nio.ByteBuffer;
11 |
12 | import org.apache.commons.lang3.ArrayUtils;
13 |
14 | /**
15 | * A convenience class wrapping around {@link ByteBuffer} used for easy
16 | * retrieval of string values
17 | *
18 | * @author Sebastian Staudt
19 | */
20 | public class PacketBuffer {
21 |
22 | private ByteBuffer byteBuffer;
23 |
24 | /**
25 | * Creates a new packet buffer from the given byte array
26 | *
27 | * @param data The data wrap into the underlying byte buffer
28 | */
29 | public PacketBuffer(byte[] data) {
30 | this.byteBuffer = ByteBuffer.wrap(data);
31 | }
32 |
33 | /**
34 | * Returns the backing byte array of the underlying byte buffer
35 | *
36 | * @return The backing byte array
37 | */
38 | public byte[] array() {
39 | return this.byteBuffer.array();
40 | }
41 |
42 | /**
43 | * Returns the next byte at the buffer's current position
44 | *
45 | * @return A byte
46 | */
47 | public byte getByte() {
48 | return this.byteBuffer.get();
49 | }
50 |
51 | /**
52 | * Returns an integer value from the buffer's current position
53 | *
54 | * @return An integer value
55 | */
56 | public int getInt() {
57 | return this.byteBuffer.getInt();
58 | }
59 |
60 | /**
61 | * Returns the length of this buffer
62 | *
63 | * @return The length of this buffer
64 | */
65 | public int getLength() {
66 | return this.byteBuffer.capacity();
67 | }
68 |
69 | /**
70 | * Returns a short integer value from the buffer's current position
71 | *
72 | * @return A short integer value
73 | */
74 | public short getShort() {
75 | return this.byteBuffer.getShort();
76 | }
77 |
78 | /**
79 | * Returns a string value from the buffer's current position
80 | *
81 | * This reads the bytes up to the first zero-byte of the underlying byte
82 | * buffer into a String
83 | *
84 | * @return A string value
85 | */
86 | public String getString() {
87 | byte[] remainingBytes = new byte[this.byteBuffer.remaining()];
88 | this.byteBuffer.slice().get(remainingBytes);
89 | int zeroPosition = ArrayUtils.indexOf(remainingBytes, (byte) 0);
90 |
91 | if (zeroPosition == ArrayUtils.INDEX_NOT_FOUND) {
92 | return null;
93 | } else {
94 | byte[] stringBytes = new byte[zeroPosition];
95 | System.arraycopy(remainingBytes, 0, stringBytes, 0, zeroPosition);
96 | this.byteBuffer.position(this.byteBuffer.position() + zeroPosition + 1);
97 |
98 | return new String(stringBytes);
99 | }
100 | }
101 |
102 | /**
103 | * Returns the number of bytes remaining in the underlying byte buffer from
104 | * the current position up to the end
105 | *
106 | * @return The number of bytes remaining in this buffer
107 | */
108 | public int remaining() {
109 | return this.byteBuffer.remaining();
110 | }
111 |
112 | /**
113 | * Returns whether there is more data available in this buffer after the
114 | * current position
115 | *
116 | * @return true if there's at least one byte left remaining
117 | */
118 | public boolean hasRemaining() {
119 | return this.byteBuffer.hasRemaining();
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2GoldenWrench.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import java.util.Date;
11 | import java.util.HashSet;
12 | import java.util.Set;
13 |
14 | import org.json.JSONArray;
15 | import org.json.JSONException;
16 | import org.json.JSONObject;
17 |
18 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
19 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
20 | import com.github.koraktor.steamcondenser.community.SteamId;
21 | import com.github.koraktor.steamcondenser.community.WebApi;
22 |
23 | /**
24 | * Represents the special Team Fortress 2 item Golden Wrench. It includes the
25 | * ID of the item, the serial number of the wrench, a reference to the SteamID
26 | * of the owner and the date this player crafted the wrench
27 | *
28 | * @author Sebastian Staudt
29 | */
30 | public class TF2GoldenWrench {
31 |
32 | private static Set goldenWrenches = null;
33 |
34 | private Date date;
35 |
36 | private int id;
37 |
38 | private int number;
39 |
40 | private SteamId owner;
41 |
42 | /**
43 | * Returns all Golden Wrenches
44 | *
45 | * @return All Golden Wrenches
46 | * @throws WebApiException if an error occurs querying the Web API
47 | * @throws SteamCondenserException if an error occurs querying the Steam
48 | * Community
49 | */
50 | public static Set getGoldenWrenches()
51 | throws SteamCondenserException {
52 | if(goldenWrenches == null) {
53 | try {
54 | goldenWrenches = new HashSet<>();
55 |
56 | JSONObject data = new JSONObject(WebApi.getJSON("ITFItems_440", "GetGoldenWrenches", 2));
57 | JSONArray wrenches = data.getJSONObject("results").getJSONArray("wrenches");
58 | for(int i = 0; i < wrenches.length(); i ++) {
59 | goldenWrenches.add(new TF2GoldenWrench(wrenches.getJSONObject(i)));
60 | }
61 | } catch(JSONException e) {
62 | throw new WebApiException("Could not parse the JSON data.", e);
63 | }
64 | }
65 |
66 | return goldenWrenches;
67 | }
68 |
69 | /**
70 | * Creates a new instance of a Golden Wrench with the given data
71 | *
72 | * @param wrenchData The JSON data for this wrench
73 | * @throws WebApiException If some attribute is missing from the JSON data
74 | * @throws SteamCondenserException If the SteamId for the owner of the
75 | * wrench cannot be created
76 | */
77 | private TF2GoldenWrench(JSONObject wrenchData)
78 | throws SteamCondenserException {
79 | try {
80 | this.date = new Date(wrenchData.getLong("timestamp"));
81 | this.id = wrenchData.getInt("itemID");
82 | this.number = wrenchData.getInt("wrenchNumber");
83 | this.owner = SteamId.create(wrenchData.getLong("steamID"), false);
84 | } catch(JSONException e) {
85 | throw new WebApiException("Could not parse JSON data.", e);
86 | }
87 | }
88 |
89 | /**
90 | * Returns the date this Golden Wrench has been crafted
91 | *
92 | * @return The crafting date of this wrench
93 | */
94 | public Date getDate() {
95 | return this.date;
96 | }
97 |
98 | /**
99 | * Returns the unique item ID of this Golden Wrench
100 | *
101 | * @return The ID of this wrench
102 | */
103 | public int getId() {
104 | return this.id;
105 | }
106 |
107 | /**
108 | * Returns the serial number of this Golden Wrench
109 | *
110 | * @return The serial of this wrench
111 | */
112 | public int getNumber() {
113 | return this.number;
114 | }
115 |
116 | /**
117 | * Returns the SteamID of the owner of this Golden Wrench
118 | *
119 | * @return The owner of this wrench
120 | */
121 | public SteamId getOwner() {
122 | return this.owner;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/l4d/L4D2Map.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.l4d;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 |
13 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
14 | import com.github.koraktor.steamcondenser.community.SteamId;
15 | import com.github.koraktor.steamcondenser.community.XMLData;
16 |
17 | /**
18 | * This class holds statistical information about a map played by a player in
19 | * Survival mode of Left4Dead 2
20 | *
21 | * The basic information provided is more or less the same for Left4Dead and
22 | * Left4Dead 2, but parsing has to be done differently.
23 | *
24 | * @author Sebastian Staudt
25 | */
26 | public class L4D2Map extends L4DMap {
27 |
28 | private static final String[] INFECTED = { "boomer", "charger", "common", "hunter", "jockey", "smoker", "spitter", "tank" };
29 |
30 | private static final String[] ITEMS = { "adrenaline", "defibs", "medkits", "pills"};
31 |
32 | private HashMap items;
33 |
34 | private HashMap kills;
35 |
36 | private boolean played;
37 |
38 | private ArrayList teammates;
39 |
40 | /**
41 | * Creates a new instance of a map based on the given XML data
42 | *
43 | * The map statistics for the Survival mode of Left4Dead 2 hold much more
44 | * information than those for Left4Dead, e.g. the teammates and items are
45 | * listed.
46 | *
47 | * @param mapData The XML data for this map
48 | */
49 | public L4D2Map(XMLData mapData)
50 | throws SteamCondenserException {
51 | String imgUrl = mapData.getString("img");
52 | this.id = imgUrl.substring(imgUrl.lastIndexOf('/'), -4);
53 | this.name = mapData.getString("name");
54 | this.played = mapData.getString("hasPlayed").equals("1");
55 |
56 | if(this.played) {
57 | this.bestTime = mapData.getFloat("besttimemilliseconds") / 1000;
58 |
59 | this.items = new HashMap<>();
60 | for(String item : ITEMS) {
61 | this.items.put(item, mapData.getInteger("items_" + item));
62 | }
63 |
64 | this.kills = new HashMap<>();
65 | for(String infected : INFECTED) {
66 | this.items.put(infected, mapData.getInteger("kills_" + infected));
67 | }
68 |
69 | this.teammates = new ArrayList<>();
70 | for(XMLData teammateData : mapData.getChildren("teammates")) {
71 | this.teammates.add(SteamId.create(teammateData.getLong()));
72 | }
73 |
74 | String medal = mapData.getString("medal");
75 | if(medal.equals("gold")) {
76 | this.medal = GOLD;
77 | } else if(medal.equals("silver")) {
78 | this.medal = SILVER;
79 | } else if(medal.equals("bronze")) {
80 | this.medal = BRONZE;
81 | } else {
82 | this.medal = NONE;
83 | }
84 | }
85 | }
86 |
87 | /**
88 | * Returns statistics about the items used by the player on this map
89 | *
90 | * @return array The items used by the player
91 | */
92 | public HashMap getItems() {
93 | return this.items;
94 | }
95 |
96 | /**
97 | * Returns the number of special infected killed by the player grouped by
98 | * the names of the special infected
99 | *
100 | * @return array The special infected killed by the player
101 | */
102 | public HashMap getKills() {
103 | return this.kills;
104 | }
105 |
106 | /**
107 | * Returns the SteamIDs of the teammates of the player in his best game on
108 | * this map
109 | *
110 | * @return array The SteamIDs of the teammates in the best game
111 | */
112 | public ArrayList getTeammates() {
113 | return this.teammates;
114 | }
115 |
116 | /**
117 | * Returns whether the player has already played this map
118 | *
119 | * @return bool true if the player has already played this map
120 | */
121 | public boolean hasPlayed() {
122 | return this.played;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Stats.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import java.util.ArrayList;
11 |
12 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
13 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
14 | import com.github.koraktor.steamcondenser.community.GameInventory;
15 | import com.github.koraktor.steamcondenser.community.GameStats;
16 | import com.github.koraktor.steamcondenser.community.XMLData;
17 |
18 | /**
19 | * This class represents the game statistics for a single user in Team Fortress
20 | * 2
21 | *
22 | * @author Sebastian Staudt
23 | */
24 | public class TF2Stats extends GameStats {
25 |
26 | private int accumulatedPoints;
27 |
28 | private ArrayList classStats;
29 |
30 | private GameInventory inventory;
31 |
32 | private int totalPlayTime;
33 |
34 | /**
35 | * Creates a new TF2Stats instance by calling the super
36 | * constructor with the game name "tf2"
37 | *
38 | * @param steamId The custom URL or 64bit Steam ID of the user
39 | * @throws SteamCondenserException if an error occurs while fetching the
40 | * stats data
41 | */
42 | public TF2Stats(Object steamId) throws SteamCondenserException {
43 | this(steamId, false);
44 | }
45 |
46 | /**
47 | * Creates a new TF2Stats instance by calling the super
48 | * constructor with the game name "tf2" (or "520"
49 | * for the beta)
50 | *
51 | * @param steamId The custom URL or 64bit Steam ID of the user
52 | * @param beta If true, creates stats for the public TF2 beta
53 | * @throws SteamCondenserException if an error occurs while fetching the
54 | * stats data
55 | */
56 | public TF2Stats(Object steamId, boolean beta) throws SteamCondenserException {
57 | super(steamId, (beta ? "520" : "tf2"));
58 |
59 | if(this.isPublic()) {
60 | this.accumulatedPoints = this.xmlData.getInteger("stats", "accumulatedPoints");
61 | this.totalPlayTime = this.xmlData.getInteger("stats", "secondsPlayedAllClassesLifetime");
62 | }
63 | }
64 |
65 | /**
66 | * Returns the total points this player has achieved in his career
67 | *
68 | * @return This player's accumulated points
69 | */
70 | public int getAccumulatedPoints() {
71 | return this.accumulatedPoints;
72 | }
73 |
74 | /**
75 | * Returns the accumulated number of seconds this player has spent playing as a TF2 class
76 | *
77 | * @return total seconds played as a TF2 class
78 | */
79 | public int getTotalPlayTime() {
80 | return this.totalPlayTime;
81 | }
82 |
83 | /**
84 | * Returns the statistics for all Team Fortress 2 classes for this user
85 | *
86 | * If the classes haven't been parsed already, parsing is done now.
87 | *
88 | * @return An array storing individual stats for each Team Fortress 2 class
89 | */
90 | public ArrayList getClassStats() {
91 | if(!this.isPublic()) {
92 | return null;
93 | }
94 |
95 | if(this.classStats == null) {
96 | this.classStats = new ArrayList<>();
97 | for(XMLData classData : this.xmlData.getElements("stats", "classData")) {
98 | this.classStats.add(TF2ClassFactory.getTF2Class(classData));
99 | }
100 | }
101 |
102 | return this.classStats;
103 | }
104 |
105 | /**
106 | * Returns the current Team Fortress 2 inventory (a.k.a. backpack) of this
107 | * player
108 | *
109 | * @return This player's TF2 backpack
110 | * @throws WebApiException If an error occured while querying Steam's Web
111 | * API
112 | */
113 | public GameInventory getInventory() throws SteamCondenserException {
114 | if(!this.isPublic()) {
115 | return null;
116 | }
117 |
118 | if(this.inventory == null) {
119 | this.inventory = GameInventory.create(this.getGame().getAppId(), this.user.getSteamId64());
120 | }
121 |
122 | return this.inventory;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/sockets/RCONSocketTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.net.InetAddress;
11 | import java.nio.ByteBuffer;
12 | import java.nio.channels.SocketChannel;
13 |
14 | import org.junit.Before;
15 | import org.junit.Rule;
16 | import org.junit.Test;
17 | import org.junit.rules.ExpectedException;
18 | import org.junit.runner.RunWith;
19 |
20 | import org.mockito.invocation.InvocationOnMock;
21 | import org.mockito.stubbing.Answer;
22 | import org.powermock.core.classloader.annotations.PrepareForTest;
23 | import org.powermock.modules.junit4.PowerMockRunner;
24 |
25 | import com.github.koraktor.steamcondenser.exceptions.ConnectionResetException;
26 | import com.github.koraktor.steamcondenser.exceptions.RCONBanException;
27 | import com.github.koraktor.steamcondenser.servers.packets.rcon.RCONPacket;
28 | import com.github.koraktor.steamcondenser.servers.packets.rcon.RCONPacketFactory;
29 |
30 | import static org.hamcrest.Matchers.is;
31 | import static org.hamcrest.Matchers.nullValue;
32 | import static org.junit.Assert.assertEquals;
33 | import static org.junit.Assert.assertNull;
34 | import static org.junit.Assert.assertThat;
35 | import static org.mockito.Mockito.doAnswer;
36 | import static org.mockito.Mockito.doReturn;
37 | import static org.mockito.Mockito.doThrow;
38 | import static org.mockito.Mockito.mock;
39 | import static org.mockito.Mockito.spy;
40 | import static org.powermock.api.mockito.PowerMockito.mockStatic;
41 | import static org.powermock.api.mockito.PowerMockito.when;
42 |
43 | /**
44 | * @author Sebastian Staudt
45 | */
46 | @RunWith(PowerMockRunner.class)
47 | @PrepareForTest(RCONPacketFactory.class)
48 | public class RCONSocketTest {
49 |
50 | @Rule
51 | private ExpectedException exception = ExpectedException.none();
52 |
53 | private RCONSocket socket;
54 |
55 | @Before
56 | public void setup() throws Exception {
57 | this.socket = spy(new RCONSocket(InetAddress.getLocalHost(), 27015));
58 | }
59 |
60 | @Test
61 | public void testNewSocket() throws Exception {
62 | assertEquals(InetAddress.getLocalHost(), this.socket.remoteSocket.getAddress());
63 | assertEquals(27015, this.socket.remoteSocket.getPort());
64 | assertNull(this.socket.channel);
65 | }
66 |
67 | @Test
68 | public void testReceiveReply() throws Exception {
69 | RCONPacket packet = mock(RCONPacket.class);
70 | mockStatic(RCONPacketFactory.class);
71 | when(RCONPacketFactory.getPacketFromData("test test".getBytes())).thenReturn(packet);
72 |
73 | doAnswer(new Answer() {
74 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
75 | RCONSocket socket = (RCONSocket) invocationOnMock.getMock();
76 | socket.buffer = ByteBuffer.wrap(new byte[]{ (byte) 0x9, 0x0, 0x0, 0x0 });
77 | return 4;
78 | }
79 | }).when(this.socket).receivePacket(4);
80 | doAnswer(new Answer() {
81 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
82 | RCONSocket socket = (RCONSocket) invocationOnMock.getMock();
83 | socket.buffer = ByteBuffer.wrap("test".getBytes());
84 | return 4;
85 | }
86 | }).when(this.socket).receivePacket(9);
87 | doAnswer(new Answer() {
88 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
89 | RCONSocket socket = (RCONSocket) invocationOnMock.getMock();
90 | socket.buffer = ByteBuffer.wrap(" test".getBytes());
91 | return 5;
92 | }
93 | }).when(this.socket).receivePacket(5);
94 |
95 | assertEquals(packet, this.socket.getReply());
96 | }
97 |
98 | @Test
99 | public void testConnectionDropped() throws Exception {
100 | this.socket.channel = SocketChannel.open();
101 | doReturn(0).when(this.socket).receivePacket(4);
102 |
103 | assertThat(this.socket.getReply(), is(nullValue()));
104 | }
105 |
106 | @Test
107 | public void testConnectionReset() throws Exception {
108 | this.socket.channel = SocketChannel.open();
109 | doThrow(new ConnectionResetException()).when(this.socket).receivePacket(4);
110 |
111 | assertThat(this.socket.getReply(), is(nullValue()));
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/dods/DoDSClass.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2009-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.dods;
9 |
10 | import com.github.koraktor.steamcondenser.community.GameClass;
11 | import com.github.koraktor.steamcondenser.community.XMLData;
12 |
13 | /**
14 | * Represents the stats for a Day of Defeat: Source class for a specific user
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class DoDSClass extends GameClass {
19 |
20 | private int blocks;
21 |
22 | private int bombsDefused;
23 |
24 | private int bombsPlanted;
25 |
26 | private int captures;
27 |
28 | private int deaths;
29 |
30 | private int dominations;
31 |
32 | private String key;
33 |
34 | private int kills;
35 |
36 | private int roundsLost;
37 |
38 | private int roundsWon;
39 |
40 | private int revenges;
41 |
42 | /**
43 | * Creates a new instance of a Day of Defeat: Source class based on the
44 | * given XML data
45 | *
46 | * @param classData The XML data of the class
47 | */
48 | public DoDSClass(XMLData classData) {
49 | this.blocks = classData.getInteger("blocks");
50 | this.bombsDefused = classData.getInteger("bombsdefused");
51 | this.bombsPlanted = classData.getInteger("bombsplanted");
52 | this.captures = classData.getInteger("captures");
53 | this.deaths = classData.getInteger("deaths");
54 | this.dominations = classData.getInteger("dominations");
55 | this.key = classData.getAttribute("key");
56 | this.kills = classData.getInteger("kills");
57 | this.name = classData.getString("name");
58 | this.playTime = classData.getInteger("playtime");
59 | this.roundsLost = classData.getInteger("roundslost");
60 | this.roundsWon = classData.getInteger("roundswon");
61 | this.revenges = classData.getInteger("revenges");
62 | }
63 |
64 | /**
65 | * Returns the blocks achieved by the player with this class
66 | *
67 | * @return The blocks achieved by the player
68 | */
69 | public int getBlocks() {
70 | return this.blocks;
71 | }
72 |
73 | /**
74 | * Returns the bombs defused by the player with this class
75 | *
76 | * @return The bombs defused by the player
77 | */
78 | public int getBombsDefuse() {
79 | return this.bombsDefused;
80 | }
81 |
82 | /**
83 | * Returns the bombs planted by the player with this class
84 | *
85 | * @return the bombs planted by the player
86 | */
87 | public int getBombsPlanted() {
88 | return this.bombsPlanted;
89 | }
90 |
91 | /**
92 | * Returns the number of points captured by the player with this class
93 | *
94 | * @return The number of points captured by the player
95 | */
96 | public int getCaptures() {
97 | return this.captures;
98 | }
99 |
100 | /**
101 | * Returns the number of times the player died with this class
102 | *
103 | * @return The number of deaths by the player
104 | */
105 | public int getDeaths() {
106 | return this.deaths;
107 | }
108 |
109 | /**
110 | * Returns the dominations achieved by the player with this class
111 | *
112 | * @return The dominations achieved by the player
113 | */
114 | public int getDominations() {
115 | return this.dominations;
116 | }
117 |
118 | /**
119 | * Returns the ID of this class
120 | *
121 | * @return The ID of this class
122 | */
123 | public String getKey() {
124 | return this.key;
125 | }
126 |
127 | /**
128 | * Returns the number of enemies killed by the player with this class
129 | *
130 | * @return The number of enemies killed by the player
131 | */
132 | public int getKills() {
133 | return this.kills;
134 | }
135 |
136 | /**
137 | * Returns the revenges achieved by the player with this class
138 | *
139 | * @return The revenges achieved by the player
140 | */
141 | public int getRevenges() {
142 | return this.revenges;
143 | }
144 |
145 | /**
146 | * Returns the number of rounds lost with this class
147 | *
148 | * @return The number of rounds lost with this class
149 | */
150 | public int getRoundsLost() {
151 | return this.roundsLost;
152 | }
153 |
154 | /**
155 | * Returns the number of rounds won with this class
156 | *
157 | * @return The number of rounds won with this class
158 | */
159 | public int getRoundsWon() {
160 | return this.roundsWon;
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/sockets/SourceSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.net.InetAddress;
11 | import java.util.ArrayList;
12 | import java.util.concurrent.TimeoutException;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
17 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
18 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacketFactory;
19 |
20 | /**
21 | * This class represents a socket used to communicate with game servers based
22 | * on the Source engine (e.g. Team Fortress 2, Counter-Strike: Source)
23 | *
24 | * @author Sebastian Staudt
25 | */
26 | public class SourceSocket extends QuerySocket {
27 |
28 | protected static final Logger LOG = LoggerFactory.getLogger(SourceSocket.class);
29 |
30 | /**
31 | * Creates a new socket to communicate with the server on the given IP
32 | * address and port
33 | *
34 | * @param ipAddress Either the IP address or the DNS name of the server
35 | * @param portNumber The port the server is listening on
36 | * @throws SteamCondenserException if the socket cannot be opened
37 | */
38 | public SourceSocket(InetAddress ipAddress, int portNumber)
39 | throws SteamCondenserException {
40 | super(ipAddress, portNumber);
41 | }
42 |
43 | /**
44 | * Reads a packet from the socket
45 | *
46 | * The Source query protocol specifies a maximum packet size of 1,400
47 | * bytes. Bigger packets will be split over several UDP packets. This
48 | * method reassembles split packets into single packet objects.
49 | * Additionally Source may compress big packets using bzip2. Those packets
50 | * will be compressed.
51 | *
52 | * @return SteamPacket The packet replied from the server
53 | * @throws SteamCondenserException if an error occurs while communicating
54 | * with the server
55 | * @throws TimeoutException if the request times out
56 | */
57 | public SteamPacket getReply()
58 | throws SteamCondenserException, TimeoutException {
59 | int bytesRead;
60 | boolean isCompressed = false;
61 | SteamPacket packet;
62 |
63 | bytesRead = this.receivePacket(1400);
64 |
65 | if(this.packetIsSplit()) {
66 | byte[] splitData;
67 | int packetCount, packetNumber, requestId, splitSize;
68 | int packetChecksum = 0;
69 | ArrayList splitPackets = new ArrayList<>();
70 |
71 | do {
72 | requestId = Integer.reverseBytes(this.buffer.getInt());
73 | isCompressed = ((requestId & 0x80000000) != 0);
74 | packetCount = this.buffer.get();
75 | packetNumber = this.buffer.get() + 1;
76 |
77 | if(isCompressed) {
78 | splitSize = Integer.reverseBytes(this.buffer.getInt());
79 | packetChecksum = Integer.reverseBytes(this.buffer.getInt());
80 | } else {
81 | splitSize = Short.reverseBytes(this.buffer.getShort());
82 | }
83 |
84 | splitData = new byte[Math.min(splitSize, this.buffer.remaining())];
85 | this.buffer.get(splitData);
86 | splitPackets.ensureCapacity(packetCount);
87 | splitPackets.add(packetNumber - 1, splitData);
88 |
89 | if(splitPackets.size() < packetCount) {
90 | try {
91 | bytesRead = this.receivePacket();
92 | } catch(TimeoutException e) {
93 | bytesRead = 0;
94 | }
95 | } else {
96 | bytesRead = 0;
97 | }
98 |
99 | LOG.info("Received packet #" + packetNumber + " of " + packetCount + " for request ID " + requestId + ".");
100 | } while(bytesRead > 0 && this.packetIsSplit());
101 |
102 | if(isCompressed) {
103 | packet = SteamPacketFactory.reassemblePacket(splitPackets, true, splitSize, packetChecksum);
104 | } else {
105 | packet = SteamPacketFactory.reassemblePacket(splitPackets);
106 | }
107 | } else {
108 | packet = this.getPacketFromData();
109 | }
110 |
111 | this.buffer.flip();
112 |
113 | if(isCompressed) {
114 | LOG.info("Received compressed reply of type \"" + packet.getClass().getSimpleName() + "\"");
115 | } else {
116 | LOG.info("Received reply of type \"" + packet.getClass().getSimpleName() + "\"");
117 | }
118 |
119 | return packet;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Class.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.community.GameClass;
11 | import com.github.koraktor.steamcondenser.community.XMLData;
12 |
13 | /**
14 | * Represents the stats for a Team Fortress 2 class for a specific user
15 | *
16 | * @author Sebastian Staudt
17 | */
18 | public class TF2Class extends GameClass {
19 |
20 | protected int maxBuildingsDestroyed;
21 | protected int maxCaptures;
22 | protected int maxDamage;
23 | protected int maxDefenses;
24 | protected int maxDominations;
25 | protected int maxKillAssists;
26 | protected int maxKills;
27 | protected int maxRevenges;
28 | protected int maxScore;
29 | protected int maxTimeAlive;
30 |
31 | /**
32 | * Creates a new TF2 class instance based on the assigned XML data
33 | *
34 | * @param classData The XML data for this class
35 | */
36 | public TF2Class(XMLData classData) {
37 | this.name = classData.getString("className");
38 | this.maxBuildingsDestroyed = classData.getInteger("ibuildingsdestroyed");
39 | this.maxCaptures = classData.getInteger("ipointcaptures");
40 | this.maxDamage = classData.getInteger("idamagedealt");
41 | this.maxDefenses = classData.getInteger("ipointdefenses");
42 | this.maxDominations = classData.getInteger("idominations");
43 | this.maxKillAssists = classData.getInteger("ikillassists");
44 | this.maxKills = classData.getInteger("inumberofkills");
45 | this.maxRevenges = classData.getInteger("irevenge");
46 | this.maxScore = classData.getInteger("ipointsscored");
47 | this.maxTimeAlive = classData.getInteger("iplaytime");
48 | this.playTime = classData.getInteger("playtimeSeconds");
49 | }
50 |
51 | /**
52 | * Returns the maximum number of buildings the player has destroyed in a
53 | * single life with this class
54 | *
55 | * @return Maximum number of buildings destroyed
56 | */
57 | public int getMaxBuildingsDestroyed() {
58 | return this.maxBuildingsDestroyed;
59 | }
60 |
61 | /**
62 | * Returns the maximum number of points captured by the player in a single
63 | * life with this class
64 | *
65 | * @return Maximum number of points captured
66 | */
67 | public int getMaxCaptures() {
68 | return this.maxCaptures;
69 | }
70 |
71 | /**
72 | * Returns the maximum damage dealt by the player in a single life with
73 | * this class
74 | *
75 | * @return Maximum damage dealt
76 | */
77 | public int getMaxDamage() {
78 | return this.maxDamage;
79 | }
80 |
81 | /**
82 | * Returns the maximum number of defenses by the player in a single life
83 | * with this class
84 | *
85 | * @return Maximum number of defenses
86 | */
87 | public int getMaxDefenses() {
88 | return this.maxDefenses;
89 | }
90 |
91 | /**
92 | * Returns the maximum number of dominations by the player in a single life
93 | * with this class
94 | *
95 | * @return Maximum number of dominations
96 | */
97 | public int getMaxDominations() {
98 | return this.maxDominations;
99 | }
100 |
101 | /**
102 | * Returns the maximum number of times the the player assisted a teammate
103 | * with killing an enemy in a single life with this class
104 | *
105 | * @return Maximum number of kill assists
106 | */
107 | public int getMaxKillAssists() {
108 | return this.maxKillAssists;
109 | }
110 |
111 | /**
112 | * Returns the maximum number of enemies killed by the player in a single
113 | * life with this class
114 | *
115 | * @return Maximum number of kills
116 | */
117 | public int getMaxKills() {
118 | return this.maxKills;
119 | }
120 |
121 | /**
122 | * Returns the maximum number of revenges by the player in a single life
123 | * with this class
124 | *
125 | * @return Maximum number of revenges
126 | */
127 | public int getMaxRevenges() {
128 | return this.maxRevenges;
129 | }
130 |
131 | /**
132 | * Returns the maximum number score achieved by the player in a single life
133 | * with this class
134 | *
135 | * @return Maximum score
136 | */
137 | public int getMaxScore() {
138 | return this.maxScore;
139 | }
140 |
141 | /**
142 | * Returns the maximum lifetime by the player in a single life with this
143 | * class
144 | *
145 | * @return Maximum lifetime
146 | */
147 | public int getMaxTimeAlive() {
148 | return this.maxTimeAlive;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/servers/sockets/RCONSocket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2008-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.io.IOException;
11 | import java.net.InetAddress;
12 | import java.nio.ByteBuffer;
13 | import java.nio.channels.SocketChannel;
14 | import java.util.concurrent.TimeoutException;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import com.github.koraktor.steamcondenser.exceptions.ConnectionResetException;
19 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
20 | import com.github.koraktor.steamcondenser.servers.packets.rcon.RCONPacket;
21 | import com.github.koraktor.steamcondenser.servers.packets.rcon.RCONPacketFactory;
22 |
23 | /**
24 | * This class represents a socket used for RCON communication with game servers
25 | * based on the Source engine (e.g. Team Fortress 2, Counter-Strike: Source)
26 | *
27 | * The Source engine uses a stateful TCP connection for RCON communication and
28 | * uses an additional socket of this type to handle RCON requests.
29 | *
30 | * @author Sebastian Staudt
31 | */
32 | public class RCONSocket extends SteamSocket {
33 |
34 | protected final static Logger LOG = LoggerFactory.getLogger(RCONSocket.class.getName());
35 |
36 | /**
37 | * Creates a new TCP socket to communicate with the server on the given IP
38 | * address and port
39 | *
40 | * @param ipAddress Either the IP address or the DNS name of the server
41 | * @param portNumber The port the server is listening on
42 | */
43 | public RCONSocket(InetAddress ipAddress, int portNumber) {
44 | super(ipAddress, portNumber);
45 | }
46 |
47 | /**
48 | * Closes the underlying TCP socket if it is connected
49 | *
50 | * @see SteamSocket#close
51 | */
52 | @Override
53 | public void close() {
54 | if (this.channel != null &&
55 | ((SocketChannel) this.channel).isConnected()) {
56 | super.close();
57 | }
58 | }
59 |
60 | /**
61 | * Sends the given RCON packet to the server
62 | *
63 | * @param dataPacket The RCON packet to send to the server
64 | * @throws SteamCondenserException if an error occurs while writing to the
65 | * socket
66 | */
67 | public void send(RCONPacket dataPacket)
68 | throws SteamCondenserException {
69 | try {
70 | if (this.channel == null ||
71 | !((SocketChannel)this.channel).isConnected()) {
72 | this.channel = SocketChannel.open();
73 | ((SocketChannel) this.channel).socket().connect(this.remoteSocket, SteamSocket.timeout);
74 | this.channel.configureBlocking(false);
75 | }
76 |
77 | this.buffer = ByteBuffer.wrap(dataPacket.getBytes());
78 | ((SocketChannel)this.channel).write(this.buffer);
79 | } catch(IOException e) {
80 | throw new SteamCondenserException(e.getMessage(), e);
81 | }
82 | }
83 |
84 | /**
85 | * Reads a packet from the socket
86 | *
87 | * The Source RCON protocol allows packets of an arbitrary sice transmitted
88 | * using multiple TCP packets. The data is received in chunks and
89 | * concatenated into a single response packet.
90 | *
91 | * @return The packet replied from the server or null if the
92 | * connection has been closed by the server
93 | * @throws SteamCondenserException if an error occurs while communicating
94 | * with the server
95 | * @throws TimeoutException if the request times out
96 | */
97 | public RCONPacket getReply()
98 | throws SteamCondenserException, TimeoutException {
99 | try {
100 | if (this.receivePacket(4) == 0) {
101 | try {
102 | this.channel.close();
103 | } catch (IOException ignored) {}
104 | return null;
105 | }
106 | } catch (ConnectionResetException e) {
107 | try {
108 | this.channel.close();
109 | } catch (IOException ignored) {}
110 | return null;
111 | }
112 |
113 | int packetSize = Integer.reverseBytes(this.buffer.getInt());
114 | int remainingBytes = packetSize;
115 |
116 | byte[] packetData = new byte[packetSize];
117 | int receivedBytes;
118 | do {
119 | receivedBytes = this.receivePacket(remainingBytes);
120 | System.arraycopy(this.buffer.array(), 0, packetData, packetSize - remainingBytes, receivedBytes);
121 | remainingBytes -= receivedBytes;
122 | } while(remainingBytes > 0);
123 |
124 | RCONPacket packet = RCONPacketFactory.getPacketFromData(packetData);
125 |
126 | LOG.info("Received packet of type \"" + packet.getClass() + "\".");
127 |
128 | return packet;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/sockets/SteamSocketTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2013, Sebastian Staudt
6 | */
7 | package com.github.koraktor.steamcondenser.servers.sockets;
8 |
9 | import java.io.IOException;
10 | import java.net.InetAddress;
11 | import java.net.UnknownHostException;
12 | import java.nio.ByteBuffer;
13 | import java.nio.channels.DatagramChannel;
14 | import java.nio.channels.SelectionKey;
15 | import java.nio.channels.Selector;
16 | import java.util.concurrent.TimeoutException;
17 |
18 | import org.junit.Before;
19 | import org.junit.Rule;
20 | import org.junit.Test;
21 | import org.junit.rules.ExpectedException;
22 | import org.junit.runner.RunWith;
23 |
24 | import org.mockito.invocation.InvocationOnMock;
25 | import org.mockito.stubbing.Answer;
26 | import org.powermock.core.classloader.annotations.PrepareForTest;
27 | import org.powermock.modules.junit4.PowerMockRunner;
28 |
29 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
30 |
31 | import static org.junit.Assert.assertEquals;
32 | import static org.mockito.Matchers.any;
33 | import static org.mockito.Mockito.verify;
34 | import static org.mockito.Mockito.when;
35 | import static org.powermock.api.mockito.PowerMockito.mock;
36 | import static org.powermock.api.mockito.PowerMockito.mockStatic;
37 |
38 | /**
39 | * @author Sebastian Staudt
40 | */
41 | @RunWith(PowerMockRunner.class)
42 | @PrepareForTest(SteamSocket.class)
43 | public class SteamSocketTest {
44 |
45 | @Rule
46 | private ExpectedException exception = ExpectedException.none();
47 |
48 | private DatagramChannel channel;
49 |
50 | private SteamSocket socket;
51 |
52 | @Before
53 | public void setup() throws Exception {
54 | this.socket = new GenericSteamSocket();
55 |
56 | this.channel = mock(DatagramChannel.class);
57 | this.socket.channel = this.channel;
58 | }
59 |
60 | @Test
61 | public void testClose() throws IOException {
62 | when(this.channel.isOpen()).thenReturn(true);
63 |
64 | this.socket.close();
65 |
66 | verify(this.channel).close();
67 | }
68 |
69 | @Test
70 | public void testReceiveIntoNewBuffer() throws Exception {
71 | Selector selector = mock(Selector.class);
72 | when(selector.select(SteamSocket.timeout)).thenReturn(1);
73 | mockStatic(Selector.class);
74 | when(Selector.open()).thenReturn(selector);
75 | when(this.channel.register(selector, SelectionKey.OP_READ)).thenReturn(null);
76 |
77 | final SteamSocket socket = this.socket;
78 | when(this.channel.read(any(ByteBuffer.class))).thenAnswer(new Answer() {
79 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
80 | socket.buffer.put("test".getBytes());
81 | return 4;
82 | }
83 | });
84 |
85 | assertEquals(4, this.socket.receivePacket(4));
86 |
87 | ByteBuffer buffer = this.socket.buffer;
88 | assertEquals(0, buffer.position());
89 | assertEquals(4, buffer.capacity());
90 | assertEquals("test", new String(buffer.array()));
91 | }
92 |
93 | @Test
94 | public void testReceiveIntoExistingBuffer() throws Exception {
95 | Selector selector = mock(Selector.class);
96 | when(selector.select(SteamSocket.timeout)).thenReturn(1);
97 | mockStatic(Selector.class);
98 | when(Selector.open()).thenReturn(selector);
99 | when(this.channel.register(selector, SelectionKey.OP_READ)).thenReturn(null);
100 |
101 | this.socket.buffer = ByteBuffer.allocate(10);
102 |
103 | final SteamSocket socket = this.socket;
104 | when(this.channel.read(any(ByteBuffer.class))).thenAnswer(new Answer() {
105 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
106 | socket.buffer.put("test".getBytes());
107 | return 4;
108 | }
109 | });
110 |
111 | assertEquals(4, this.socket.receivePacket(4));
112 |
113 | ByteBuffer buffer = this.socket.buffer;
114 | assertEquals(0, buffer.position());
115 | assertEquals(4, buffer.capacity());
116 | assertEquals("test", new String(buffer.array()));
117 | }
118 |
119 | @Test
120 | public void testSetTimeout() {
121 | SteamSocket.setTimeout(2000);
122 |
123 | assertEquals(2000, SteamSocket.timeout);
124 | }
125 |
126 | @Test
127 | public void testTimeout() throws Exception {
128 | this.exception.expect(TimeoutException.class);
129 |
130 | Selector selector = mock(Selector.class);
131 | when(selector.select(SteamSocket.timeout)).thenReturn(0);
132 | mockStatic(Selector.class);
133 | when(Selector.open()).thenReturn(selector);
134 | when(this.channel.register(selector, SelectionKey.OP_READ)).thenReturn(null);
135 |
136 | this.socket.receivePacket(4);
137 | }
138 |
139 | class GenericSteamSocket extends SteamSocket {
140 |
141 | public GenericSteamSocket() throws UnknownHostException {
142 | super(InetAddress.getLocalHost(), 27015);
143 | }
144 |
145 | public SteamPacket getReply() {
146 | return null;
147 | }
148 |
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/test/resources/com/github/koraktor/steamcondenser/community/koraktor-friends.json:
--------------------------------------------------------------------------------
1 | {
2 | "friendslist": {
3 | "friends": [{
4 | "steamid": "76561197960299812",
5 | "relationship": "friend",
6 | "friend_since": 0
7 | }, {
8 | "steamid": "76561197960377091",
9 | "relationship": "friend",
10 | "friend_since": 0
11 | }, {
12 | "steamid": "76561197960811440",
13 | "relationship": "friend",
14 | "friend_since": 0
15 | }, {
16 | "steamid": "76561197961445668",
17 | "relationship": "friend",
18 | "friend_since": 0
19 | }, {
20 | "steamid": "76561197962380601",
21 | "relationship": "friend",
22 | "friend_since": 0
23 | }, {
24 | "steamid": "76561197964238198",
25 | "relationship": "friend",
26 | "friend_since": 0
27 | }, {
28 | "steamid": "76561197965401776",
29 | "relationship": "friend",
30 | "friend_since": 0
31 | }, {
32 | "steamid": "76561197967234643",
33 | "relationship": "friend",
34 | "friend_since": 0
35 | }, {
36 | "steamid": "76561197968567369",
37 | "relationship": "friend",
38 | "friend_since": 0
39 | }, {
40 | "steamid": "76561197968573709",
41 | "relationship": "friend",
42 | "friend_since": 1330018231
43 | }, {
44 | "steamid": "76561197968608003",
45 | "relationship": "friend",
46 | "friend_since": 0
47 | }, {
48 | "steamid": "76561197968747750",
49 | "relationship": "friend",
50 | "friend_since": 1279986266
51 | }, {
52 | "steamid": "76561197969318941",
53 | "relationship": "friend",
54 | "friend_since": 0
55 | }, {
56 | "steamid": "76561197969355234",
57 | "relationship": "friend",
58 | "friend_since": 0
59 | }, {
60 | "steamid": "76561197969802605",
61 | "relationship": "friend",
62 | "friend_since": 0
63 | }, {
64 | "steamid": "76561197970043247",
65 | "relationship": "friend",
66 | "friend_since": 0
67 | }, {
68 | "steamid": "76561197970084764",
69 | "relationship": "friend",
70 | "friend_since": 0
71 | }, {
72 | "steamid": "76561197970367346",
73 | "relationship": "friend",
74 | "friend_since": 0
75 | }, {
76 | "steamid": "76561197970371661",
77 | "relationship": "friend",
78 | "friend_since": 0
79 | }, {
80 | "steamid": "76561197970557916",
81 | "relationship": "friend",
82 | "friend_since": 0
83 | }, {
84 | "steamid": "76561197970610839",
85 | "relationship": "friend",
86 | "friend_since": 0
87 | }, {
88 | "steamid": "76561197970732484",
89 | "relationship": "friend",
90 | "friend_since": 0
91 | }, {
92 | "steamid": "76561197971785868",
93 | "relationship": "friend",
94 | "friend_since": 0
95 | }, {
96 | "steamid": "76561197975302059",
97 | "relationship": "friend",
98 | "friend_since": 0
99 | }, {
100 | "steamid": "76561197977072501",
101 | "relationship": "friend",
102 | "friend_since": 0
103 | }, {
104 | "steamid": "76561197977586822",
105 | "relationship": "friend",
106 | "friend_since": 1290718284
107 | }, {
108 | "steamid": "76561197978628218",
109 | "relationship": "friend",
110 | "friend_since": 1345215624
111 | }, {
112 | "steamid": "76561197979277905",
113 | "relationship": "friend",
114 | "friend_since": 1292607952
115 | }, {
116 | "steamid": "76561197980055115",
117 | "relationship": "friend",
118 | "friend_since": 1330719963
119 | }, {
120 | "steamid": "76561197980238112",
121 | "relationship": "friend",
122 | "friend_since": 0
123 | }, {
124 | "steamid": "76561197983311154",
125 | "relationship": "friend",
126 | "friend_since": 1279932515
127 | }, {
128 | "steamid": "76561197984496388",
129 | "relationship": "friend",
130 | "friend_since": 0
131 | }, {
132 | "steamid": "76561197986233889",
133 | "relationship": "friend",
134 | "friend_since": 1290718281
135 | }, {
136 | "steamid": "76561197989761999",
137 | "relationship": "friend",
138 | "friend_since": 1346751372
139 | }, {
140 | "steamid": "76561197990484599",
141 | "relationship": "friend",
142 | "friend_since": 1298211895
143 | }, {
144 | "steamid": "76561197992422064",
145 | "relationship": "friend",
146 | "friend_since": 1327745215
147 | }, {
148 | "steamid": "76561197993236014",
149 | "relationship": "friend",
150 | "friend_since": 1332898411
151 | }, {
152 | "steamid": "76561197994013896",
153 | "relationship": "friend",
154 | "friend_since": 1325179388
155 | }, {
156 | "steamid": "76561197998540387",
157 | "relationship": "friend",
158 | "friend_since": 0
159 | }, {
160 | "steamid": "76561198000503301",
161 | "relationship": "friend",
162 | "friend_since": 1309703220
163 | }, {
164 | "steamid": "76561198005170109",
165 | "relationship": "friend",
166 | "friend_since": 1345664691
167 | }, {
168 | "steamid": "76561198005731671",
169 | "relationship": "friend",
170 | "friend_since": 1286378684
171 | }, {
172 | "steamid": "76561198037444970",
173 | "relationship": "friend",
174 | "friend_since": 1335979528
175 | }]
176 |
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/test/java/com/github/koraktor/steamcondenser/servers/sockets/SourceSocketTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2018, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.servers.sockets;
9 |
10 | import java.net.InetAddress;
11 | import java.nio.ByteBuffer;
12 | import java.util.ArrayList;
13 |
14 | import org.junit.Before;
15 | import org.junit.Test;
16 | import org.junit.runner.RunWith;
17 |
18 | import org.mockito.invocation.InvocationOnMock;
19 | import org.mockito.stubbing.Answer;
20 | import org.powermock.core.classloader.annotations.PrepareForTest;
21 | import org.powermock.modules.junit4.PowerMockRunner;
22 |
23 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacket;
24 | import com.github.koraktor.steamcondenser.servers.packets.SteamPacketFactory;
25 |
26 | import static org.junit.Assert.assertEquals;
27 | import static org.mockito.Matchers.eq;
28 | import static org.mockito.Matchers.refEq;
29 | import static org.mockito.Mockito.mock;
30 | import static org.mockito.Mockito.spy;
31 | import static org.powermock.api.mockito.PowerMockito.doAnswer;
32 | import static org.powermock.api.mockito.PowerMockito.mockStatic;
33 | import static org.powermock.api.mockito.PowerMockito.when;
34 |
35 | /**
36 | * @author Sebastian Staudt
37 | */
38 | @RunWith(PowerMockRunner.class)
39 | @PrepareForTest(SteamPacketFactory.class)
40 | public class SourceSocketTest {
41 |
42 | private SteamPacket packet;
43 |
44 | private SourceSocket socket;
45 |
46 | @Before
47 | public void setup() throws Exception {
48 | this.packet = mock(SteamPacket.class);
49 | this.socket = spy(new SourceSocket(InetAddress.getLocalHost(), 27015));
50 |
51 | mockStatic(SteamPacketFactory.class);
52 | }
53 |
54 | @Test
55 | public void testSinglePacketReply() throws Exception {
56 | when(SteamPacketFactory.getPacketFromData("test".getBytes())).thenReturn(this.packet);
57 |
58 | doAnswer(new Answer() {
59 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
60 | SourceSocket socket = (SourceSocket) invocationOnMock.getMock();
61 | socket.buffer = ByteBuffer.wrap(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 't', 'e', 's', 't' });
62 | return 8;
63 | }
64 | }).when(this.socket).receivePacket(1400);
65 |
66 | assertEquals(this.packet, this.socket.getReply());
67 | }
68 |
69 | @Test
70 | public void testSplitPacketReply() throws Exception {
71 | ArrayList packets = new ArrayList<>();
72 | packets.add("test".getBytes());
73 | packets.add("test".getBytes());
74 | when(SteamPacketFactory.reassemblePacket(refEq(packets))).thenReturn(this.packet);
75 |
76 | doAnswer(new Answer() {
77 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
78 | SourceSocket socket = (SourceSocket) invocationOnMock.getMock();
79 | socket.buffer = ByteBuffer.wrap(new byte[] { (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xD2, 0x4, 0x0, 0x0, 0x2, 0x0, 0x4, 0x0, 't', 'e', 's', 't' });
80 | return 1400;
81 | }
82 | }).when(this.socket).receivePacket(1400);
83 | doAnswer(new Answer() {
84 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
85 | SourceSocket socket = (SourceSocket) invocationOnMock.getMock();
86 | socket.buffer = ByteBuffer.wrap(new byte[]{(byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xD2, 0x4, 0x0, 0x0, 0x2, 0x1, 0x4, 0x0, 't', 'e', 's', 't'});
87 | return 1400;
88 | }
89 | }).when(this.socket).receivePacket();
90 |
91 | assertEquals(this.packet, this.socket.getReply());
92 | }
93 |
94 | @Test
95 | public void testCompressedReply() throws Exception {
96 | ArrayList packets = new ArrayList<>();
97 | packets.add("test".getBytes());
98 | packets.add("test".getBytes());
99 | when(SteamPacketFactory.reassemblePacket(refEq(packets), eq(true), eq(8), eq(1337))).thenReturn(this.packet);
100 |
101 | doAnswer(new Answer() {
102 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
103 | SourceSocket socket = (SourceSocket) invocationOnMock.getMock();
104 | socket.buffer = ByteBuffer.wrap(new byte[] { (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xD2, 0x4, 0x0, (byte) 0x80, 0x2, 0x0, 0x8, 0x0, 0x0, 0x0, 0x39, 0x5, 0x0, 0x0, 't', 'e', 's', 't' });
105 | return 1400;
106 | }
107 | }).when(this.socket).receivePacket(1400);
108 | doAnswer(new Answer() {
109 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
110 | SourceSocket socket = (SourceSocket) invocationOnMock.getMock();
111 | socket.buffer = ByteBuffer.wrap(new byte[]{(byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xD2, 0x4, 0x0, (byte) 0x80, 0x2, 0x1, 0x8, 0x0, 0x0, 0x0, 0x39, 0x5, 0x0, 0x0, 't', 'e', 's', 't'});
112 | return 1400;
113 | }
114 | }).when(this.socket).receivePacket();
115 |
116 | assertEquals(this.packet, this.socket.getReply());
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/dota2/Dota2Inventory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.dota2;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
12 | import com.github.koraktor.steamcondenser.community.GameInventory;
13 | import com.github.koraktor.steamcondenser.community.GameItem;
14 |
15 | /**
16 | * Represents the inventory of a player of DotA 2
17 | *
18 | * @author Sebastian Staudt
19 | */
20 | public class Dota2Inventory extends GameInventory {
21 |
22 | public static final int APP_ID = 570;
23 |
24 | /**
25 | * This checks the cache for an existing inventory. If it exists it is
26 | * returned. Otherwise a new inventory is created.
27 | *
28 | * @param vanityUrl The vanity URL of the user
29 | * @return The DotA 2 inventory for the given user
30 | * @throws SteamCondenserException if creating the inventory fails
31 | */
32 | public static Dota2Inventory create(String vanityUrl)
33 | throws SteamCondenserException {
34 | return (Dota2Inventory) create(APP_ID, vanityUrl, true, false);
35 | }
36 |
37 | /**
38 | * This checks the cache for an existing inventory. If it exists it is
39 | * returned. Otherwise a new inventory is created.
40 | *
41 | * @param steamId64 The 64bit Steam ID of the user
42 | * @return The DotA 2 inventory for the given user
43 | * @throws SteamCondenserException if creating the inventory fails
44 | */
45 | public static Dota2Inventory create(long steamId64)
46 | throws SteamCondenserException {
47 | return (Dota2Inventory) create(APP_ID, steamId64, true, false);
48 | }
49 |
50 | /**
51 | * This checks the cache for an existing inventory. If it exists it is
52 | * returned. Otherwise a new inventory is created.
53 | *
54 | * @param vanityUrl The vanity URL of the user
55 | * @param fetchNow Whether the data should be fetched now
56 | * @return The DotA 2 inventory for the given user
57 | * @throws SteamCondenserException if creating the inventory fails
58 | */
59 | public static Dota2Inventory create(String vanityUrl, boolean fetchNow)
60 | throws SteamCondenserException {
61 | return (Dota2Inventory) create(APP_ID, vanityUrl, fetchNow, false);
62 | }
63 |
64 | /**
65 | * This checks the cache for an existing inventory. If it exists it is
66 | * returned. Otherwise a new inventory is created.
67 | *
68 | * @param steamId64 The 64bit Steam ID of the user
69 | * @param fetchNow Whether the data should be fetched now
70 | * @return The DotA 2 inventory for the given user
71 | * @throws SteamCondenserException if creating the inventory fails
72 | */
73 | public static Dota2Inventory create(long steamId64, boolean fetchNow)
74 | throws SteamCondenserException {
75 | return (Dota2Inventory) create(APP_ID, steamId64, fetchNow, false);
76 | }
77 |
78 | /**
79 | * This checks the cache for an existing inventory. If it exists it is
80 | * returned. Otherwise a new inventory is created.
81 | *
82 | * @param vanityUrl The vanity URL of the user
83 | * @param fetchNow Whether the data should be fetched now
84 | * @param bypassCache Whether the cache should be bypassed
85 | * @return The DotA 2 inventory for the given user
86 | * @throws SteamCondenserException if creating the inventory fails
87 | */
88 | public static Dota2Inventory create(String vanityUrl, boolean fetchNow, boolean bypassCache)
89 | throws SteamCondenserException {
90 | return (Dota2Inventory) create(APP_ID, vanityUrl, fetchNow, bypassCache);
91 | }
92 |
93 | /**
94 | * This checks the cache for an existing inventory. If it exists it is
95 | * returned. Otherwise a new inventory is created.
96 | *
97 | * @param steamId64 The 64bit Steam ID of the user
98 | * @param fetchNow Whether the data should be fetched now
99 | * @param bypassCache Whether the cache should be bypassed
100 | * @return The DotA 2 inventory for the given user
101 | * @throws SteamCondenserException if creating the inventory fails
102 | */
103 | public static Dota2Inventory create(long steamId64, boolean fetchNow, boolean bypassCache)
104 | throws SteamCondenserException {
105 | return (Dota2Inventory) create(APP_ID, steamId64, fetchNow, bypassCache);
106 | }
107 |
108 | /**
109 | * Creates a new inventory instance for the player with the given Steam ID
110 | *
111 | * @param steamId64 The 64bit Steam ID of the user
112 | * @param fetchNow Whether the data should be fetched now
113 | * @see GameInventory#create
114 | * @throws WebApiException on Web API errors
115 | */
116 | public Dota2Inventory(long steamId64, boolean fetchNow)
117 | throws SteamCondenserException {
118 | super(APP_ID, steamId64, fetchNow);
119 | }
120 |
121 | /**
122 | * Returns the item class for DotA 2
123 | *
124 | * @return The item class for DotA 2 is Dota2Item
125 | * @see Dota2Item
126 | */
127 | protected Class extends GameItem> getItemClass() {
128 | return Dota2Item.class;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2Inventory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2010-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
12 | import com.github.koraktor.steamcondenser.community.GameInventory;
13 | import com.github.koraktor.steamcondenser.community.GameItem;
14 |
15 | /**
16 | * Represents the inventory (aka. Backpack) of a Team Fortress 2 player
17 | *
18 | * @author Sebastian Staudt
19 | */
20 | public class TF2Inventory extends GameInventory {
21 |
22 | public static final int APP_ID = 440;
23 |
24 | /**
25 | * This checks the cache for an existing inventory. If it exists it is
26 | * returned. Otherwise a new inventory is created.
27 | *
28 | * @param vanityUrl The vanity URL of the user
29 | * @return The Team Fortress 2 inventory for the given user
30 | * @throws SteamCondenserException if creating the inventory fails
31 | */
32 | public static TF2Inventory create(String vanityUrl)
33 | throws SteamCondenserException {
34 | return (TF2Inventory) create(APP_ID, vanityUrl, true, false);
35 | }
36 |
37 | /**
38 | * This checks the cache for an existing inventory. If it exists it is
39 | * returned. Otherwise a new inventory is created.
40 | *
41 | * @param steamId64 The 64bit Steam ID of the user
42 | * @return The Team Fortress 2 inventory for the given user
43 | * @throws SteamCondenserException if creating the inventory fails
44 | */
45 | public static TF2Inventory create(long steamId64)
46 | throws SteamCondenserException {
47 | return (TF2Inventory) create(APP_ID, steamId64, true, false);
48 | }
49 |
50 | /**
51 | * This checks the cache for an existing inventory. If it exists it is
52 | * returned. Otherwise a new inventory is created.
53 | *
54 | * @param vanityUrl The vanity URL of the user
55 | * @param fetchNow Whether the data should be fetched now
56 | * @return The Team Fortress 2 inventory for the given user
57 | * @throws SteamCondenserException if creating the inventory fails
58 | */
59 | public static TF2Inventory create(String vanityUrl, boolean fetchNow)
60 | throws SteamCondenserException {
61 | return (TF2Inventory) create(APP_ID, vanityUrl, fetchNow, false);
62 | }
63 |
64 | /**
65 | * This checks the cache for an existing inventory. If it exists it is
66 | * returned. Otherwise a new inventory is created.
67 | *
68 | * @param steamId64 The 64bit Steam ID of the user
69 | * @param fetchNow Whether the data should be fetched now
70 | * @return The Team Fortress 2 inventory for the given user
71 | * @throws SteamCondenserException if creating the inventory fails
72 | */
73 | public static TF2Inventory create(long steamId64, boolean fetchNow)
74 | throws SteamCondenserException {
75 | return (TF2Inventory) create(APP_ID, steamId64, fetchNow, false);
76 | }
77 |
78 | /**
79 | * This checks the cache for an existing inventory. If it exists it is
80 | * returned. Otherwise a new inventory is created.
81 | *
82 | * @param vanityUrl The vanity URL of the user
83 | * @param fetchNow Whether the data should be fetched now
84 | * @param bypassCache Whether the cache should be bypassed
85 | * @return The Team Fortress 2 inventory for the given user
86 | * @throws SteamCondenserException if creating the inventory fails
87 | */
88 | public static TF2Inventory create(String vanityUrl, boolean fetchNow, boolean bypassCache)
89 | throws SteamCondenserException {
90 | return (TF2Inventory) create(APP_ID, vanityUrl, fetchNow, bypassCache);
91 | }
92 |
93 | /**
94 | * This checks the cache for an existing inventory. If it exists it is
95 | * returned. Otherwise a new inventory is created.
96 | *
97 | * @param steamId64 The 64bit Steam ID of the user
98 | * @param fetchNow Whether the data should be fetched now
99 | * @param bypassCache Whether the cache should be bypassed
100 | * @return The Team Fortress 2 inventory for the given user
101 | * @throws SteamCondenserException if creating the inventory fails
102 | */
103 | public static TF2Inventory create(long steamId64, boolean fetchNow, boolean bypassCache)
104 | throws SteamCondenserException {
105 | return (TF2Inventory) create(APP_ID, steamId64, fetchNow, bypassCache);
106 | }
107 |
108 | /**
109 | * Creates a new inventory instance for the player with the given Steam ID
110 | *
111 | * @param steamId64 The 64bit Steam ID of the user
112 | * @param fetchNow Whether the data should be fetched now
113 | * @see GameInventory#create
114 | * @throws WebApiException on Web API errors
115 | */
116 | public TF2Inventory(long steamId64, boolean fetchNow)
117 | throws SteamCondenserException {
118 | super(APP_ID, steamId64, fetchNow);
119 | }
120 |
121 | /**
122 | * Returns the item class for Team Fortress 2
123 | *
124 | * @return The item class for Team Fortress 2 is TF2Item
125 | * @see TF2Item
126 | */
127 | protected Class extends GameItem> getItemClass() {
128 | return TF2Item.class;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/portal2/Portal2Inventory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.portal2;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
12 | import com.github.koraktor.steamcondenser.community.GameInventory;
13 | import com.github.koraktor.steamcondenser.community.GameItem;
14 |
15 | /**
16 | * Represents the inventory (aka. Robot Enrichment) of a Portal 2 player
17 | *
18 | * @author Sebastian Staudt
19 | */
20 | public class Portal2Inventory extends GameInventory {
21 |
22 | public static final int APP_ID = 620;
23 |
24 | /**
25 | * This checks the cache for an existing inventory. If it exists it is
26 | * returned. Otherwise a new inventory is created.
27 | *
28 | * @param vanityUrl The vanity URL of the user
29 | * @return The Portal 2 inventory for the given user
30 | * @throws SteamCondenserException if creating the inventory fails
31 | */
32 | public static Portal2Inventory create(String vanityUrl)
33 | throws SteamCondenserException {
34 | return (Portal2Inventory) create(APP_ID, vanityUrl, true, false);
35 | }
36 |
37 | /**
38 | * This checks the cache for an existing inventory. If it exists it is
39 | * returned. Otherwise a new inventory is created.
40 | *
41 | * @param steamId64 The 64bit Steam ID of the user
42 | * @return The Portal 2 inventory for the given user
43 | * @throws SteamCondenserException if creating the inventory fails
44 | */
45 | public static Portal2Inventory create(long steamId64)
46 | throws SteamCondenserException {
47 | return (Portal2Inventory) create(APP_ID, steamId64, true, false);
48 | }
49 |
50 | /**
51 | * This checks the cache for an existing inventory. If it exists it is
52 | * returned. Otherwise a new inventory is created.
53 | *
54 | * @param vanityUrl The vanity URL of the user
55 | * @param fetchNow Whether the data should be fetched now
56 | * @return The Portal 2 inventory for the given user
57 | * @throws SteamCondenserException if creating the inventory fails
58 | */
59 | public static Portal2Inventory create(String vanityUrl, boolean fetchNow)
60 | throws SteamCondenserException {
61 | return (Portal2Inventory) create(APP_ID, vanityUrl, fetchNow, false);
62 | }
63 |
64 | /**
65 | * This checks the cache for an existing inventory. If it exists it is
66 | * returned. Otherwise a new inventory is created.
67 | *
68 | * @param steamId64 The 64bit Steam ID of the user
69 | * @param fetchNow Whether the data should be fetched now
70 | * @return The Portal 2 inventory for the given user
71 | * @throws SteamCondenserException if creating the inventory fails
72 | */
73 | public static Portal2Inventory create(long steamId64, boolean fetchNow)
74 | throws SteamCondenserException {
75 | return (Portal2Inventory) create(APP_ID, steamId64, fetchNow, false);
76 | }
77 |
78 | /**
79 | * This checks the cache for an existing inventory. If it exists it is
80 | * returned. Otherwise a new inventory is created.
81 | *
82 | * @param vanityUrl The vanity URL of the user
83 | * @param fetchNow Whether the data should be fetched now
84 | * @param bypassCache Whether the cache should be bypassed
85 | * @return The Portal 2 inventory for the given user
86 | * @throws SteamCondenserException if creating the inventory fails
87 | */
88 | public static Portal2Inventory create(String vanityUrl, boolean fetchNow, boolean bypassCache)
89 | throws SteamCondenserException {
90 | return (Portal2Inventory) create(APP_ID, vanityUrl, fetchNow, bypassCache);
91 | }
92 |
93 | /**
94 | * This checks the cache for an existing inventory. If it exists it is
95 | * returned. Otherwise a new inventory is created.
96 | *
97 | * @param steamId64 The 64bit Steam ID of the user
98 | * @param fetchNow Whether the data should be fetched now
99 | * @param bypassCache Whether the cache should be bypassed
100 | * @return The Portal 2 inventory for the given user
101 | * @throws SteamCondenserException if creating the inventory fails
102 | */
103 | public static Portal2Inventory create(long steamId64, boolean fetchNow, boolean bypassCache)
104 | throws SteamCondenserException {
105 | return (Portal2Inventory) create(APP_ID, steamId64, fetchNow, bypassCache);
106 | }
107 |
108 | /**
109 | * Creates a new inventory instance for the player with the given Steam ID
110 | *
111 | * @param steamId64 The 64bit Steam ID of the user
112 | * @param fetchNow Whether the data should be fetched now
113 | * @see GameInventory#create
114 | * @throws WebApiException on Web API errors
115 | */
116 | public Portal2Inventory(long steamId64, boolean fetchNow)
117 | throws SteamCondenserException {
118 | super(APP_ID, steamId64, fetchNow);
119 | }
120 |
121 | /**
122 | * Returns the item class for Portal 2
123 | *
124 | * @return The item class for Portal 2 is Portal2Item
125 | * @see Portal2Item
126 | */
127 | protected Class extends GameItem> getItemClass() {
128 | return Portal2Item.class;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/dota2/Dota2BetaInventory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2012-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.dota2;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
12 | import com.github.koraktor.steamcondenser.community.GameInventory;
13 | import com.github.koraktor.steamcondenser.community.GameItem;
14 |
15 | /**
16 | * Represents the inventory of a player of the DotA 2 beta
17 | *
18 | * @author Sebastian Staudt
19 | */
20 | public class Dota2BetaInventory extends GameInventory {
21 |
22 | public static final int APP_ID = 205790;
23 |
24 | /**
25 | * This checks the cache for an existing inventory. If it exists it is
26 | * returned. Otherwise a new inventory is created.
27 | *
28 | * @param vanityUrl The vanity URL of the user
29 | * @return The DotA 2 Beta inventory for the given user
30 | * @throws SteamCondenserException if creating the inventory fails
31 | */
32 | public static Dota2BetaInventory create(String vanityUrl)
33 | throws SteamCondenserException {
34 | return (Dota2BetaInventory) create(APP_ID, vanityUrl, true, false);
35 | }
36 |
37 | /**
38 | * This checks the cache for an existing inventory. If it exists it is
39 | * returned. Otherwise a new inventory is created.
40 | *
41 | * @param steamId64 The 64bit Steam ID of the user
42 | * @return The DotA 2 Beta inventory for the given user
43 | * @throws SteamCondenserException if creating the inventory fails
44 | */
45 | public static Dota2BetaInventory create(long steamId64)
46 | throws SteamCondenserException {
47 | return (Dota2BetaInventory) create(APP_ID, steamId64, true, false);
48 | }
49 |
50 | /**
51 | * This checks the cache for an existing inventory. If it exists it is
52 | * returned. Otherwise a new inventory is created.
53 | *
54 | * @param vanityUrl The vanity URL of the user
55 | * @param fetchNow Whether the data should be fetched now
56 | * @return The DotA 2 Beta inventory for the given user
57 | * @throws SteamCondenserException if creating the inventory fails
58 | */
59 | public static Dota2BetaInventory create(String vanityUrl, boolean fetchNow)
60 | throws SteamCondenserException {
61 | return (Dota2BetaInventory) create(APP_ID, vanityUrl, fetchNow, false);
62 | }
63 |
64 | /**
65 | * This checks the cache for an existing inventory. If it exists it is
66 | * returned. Otherwise a new inventory is created.
67 | *
68 | * @param steamId64 The 64bit Steam ID of the user
69 | * @param fetchNow Whether the data should be fetched now
70 | * @return The DotA 2 Beta inventory for the given user
71 | * @throws SteamCondenserException if creating the inventory fails
72 | */
73 | public static Dota2BetaInventory create(long steamId64, boolean fetchNow)
74 | throws SteamCondenserException {
75 | return (Dota2BetaInventory) create(APP_ID, steamId64, fetchNow, false);
76 | }
77 |
78 | /**
79 | * This checks the cache for an existing inventory. If it exists it is
80 | * returned. Otherwise a new inventory is created.
81 | *
82 | * @param vanityUrl The vanity URL of the user
83 | * @param fetchNow Whether the data should be fetched now
84 | * @param bypassCache Whether the cache should be bypassed
85 | * @return The DotA 2 Beta inventory for the given user
86 | * @throws SteamCondenserException if creating the inventory fails
87 | */
88 | public static Dota2BetaInventory create(String vanityUrl, boolean fetchNow, boolean bypassCache)
89 | throws SteamCondenserException {
90 | return (Dota2BetaInventory) create(APP_ID, vanityUrl, fetchNow, bypassCache);
91 | }
92 |
93 | /**
94 | * This checks the cache for an existing inventory. If it exists it is
95 | * returned. Otherwise a new inventory is created.
96 | *
97 | * @param steamId64 The 64bit Steam ID of the user
98 | * @param fetchNow Whether the data should be fetched now
99 | * @param bypassCache Whether the cache should be bypassed
100 | * @return The DotA 2 Beta inventory for the given user
101 | * @throws SteamCondenserException if creating the inventory fails
102 | */
103 | public static Dota2BetaInventory create(long steamId64, boolean fetchNow, boolean bypassCache)
104 | throws SteamCondenserException {
105 | return (Dota2BetaInventory) create(APP_ID, steamId64, fetchNow, bypassCache);
106 | }
107 |
108 | /**
109 | * Creates a new inventory instance for the player with the given Steam ID
110 | *
111 | * @param steamId64 The 64bit Steam ID of the user
112 | * @param fetchNow Whether the data should be fetched now
113 | * @see GameInventory#create
114 | * @throws WebApiException on Web API errors
115 | */
116 | public Dota2BetaInventory(long steamId64, boolean fetchNow)
117 | throws SteamCondenserException {
118 | super(APP_ID, steamId64, fetchNow);
119 | }
120 |
121 | /**
122 | * Returns the item class for DotA 2
123 | *
124 | * @return The item class for DotA 2 is Dota2Item
125 | * @see Dota2Item
126 | */
127 | protected Class extends GameItem> getItemClass() {
128 | return Dota2Item.class;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/com/github/koraktor/steamcondenser/community/tf2/TF2BetaInventory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This code is free software; you can redistribute it and/or modify it under
3 | * the terms of the new BSD License.
4 | *
5 | * Copyright (c) 2011-2013, Sebastian Staudt
6 | */
7 |
8 | package com.github.koraktor.steamcondenser.community.tf2;
9 |
10 | import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
11 | import com.github.koraktor.steamcondenser.exceptions.WebApiException;
12 | import com.github.koraktor.steamcondenser.community.GameInventory;
13 | import com.github.koraktor.steamcondenser.community.GameItem;
14 |
15 | /**
16 | * Represents the inventory (aka. Backpack) of a player of the public Team
17 | * Fortress 2 beta
18 | *
19 | * @author Sebastian Staudt
20 | */
21 | public class TF2BetaInventory extends GameInventory {
22 |
23 | public static final int APP_ID = 520;
24 |
25 | /**
26 | * This checks the cache for an existing inventory. If it exists it is
27 | * returned. Otherwise a new inventory is created.
28 | *
29 | * @param vanityUrl The vanity URL of the user
30 | * @return The Team Fortress 2 Beta inventory for the given user
31 | * @throws SteamCondenserException if creating the inventory fails
32 | */
33 | public static TF2BetaInventory create(String vanityUrl)
34 | throws SteamCondenserException {
35 | return (TF2BetaInventory) create(APP_ID, vanityUrl, true, false);
36 | }
37 |
38 | /**
39 | * This checks the cache for an existing inventory. If it exists it is
40 | * returned. Otherwise a new inventory is created.
41 | *
42 | * @param steamId64 The 64bit Steam ID of the user
43 | * @return The Team Fortress 2 Beta inventory for the given user
44 | * @throws SteamCondenserException if creating the inventory fails
45 | */
46 | public static TF2BetaInventory create(long steamId64)
47 | throws SteamCondenserException {
48 | return (TF2BetaInventory) create(APP_ID, steamId64, true, false);
49 | }
50 |
51 | /**
52 | * This checks the cache for an existing inventory. If it exists it is
53 | * returned. Otherwise a new inventory is created.
54 | *
55 | * @param vanityUrl The vanity URL of the user
56 | * @param fetchNow Whether the data should be fetched now
57 | * @return The Team Fortress 2 Beta inventory for the given user
58 | * @throws SteamCondenserException if creating the inventory fails
59 | */
60 | public static TF2BetaInventory create(String vanityUrl, boolean fetchNow)
61 | throws SteamCondenserException {
62 | return (TF2BetaInventory) create(APP_ID, vanityUrl, fetchNow, false);
63 | }
64 |
65 | /**
66 | * This checks the cache for an existing inventory. If it exists it is
67 | * returned. Otherwise a new inventory is created.
68 | *
69 | * @param steamId64 The 64bit Steam ID of the user
70 | * @param fetchNow Whether the data should be fetched now
71 | * @return The Team Fortress 2 Beta inventory for the given user
72 | * @throws SteamCondenserException if creating the inventory fails
73 | */
74 | public static TF2BetaInventory create(long steamId64, boolean fetchNow)
75 | throws SteamCondenserException {
76 | return (TF2BetaInventory) create(APP_ID, steamId64, fetchNow, false);
77 | }
78 |
79 | /**
80 | * This checks the cache for an existing inventory. If it exists it is
81 | * returned. Otherwise a new inventory is created.
82 | *
83 | * @param vanityUrl The vanity URL of the user
84 | * @param fetchNow Whether the data should be fetched now
85 | * @param bypassCache Whether the cache should be bypassed
86 | * @return The Team Fortress 2 Beta inventory for the given user
87 | * @throws SteamCondenserException if creating the inventory fails
88 | */
89 | public static TF2BetaInventory create(String vanityUrl, boolean fetchNow, boolean bypassCache)
90 | throws SteamCondenserException {
91 | return (TF2BetaInventory) create(APP_ID, vanityUrl, fetchNow, bypassCache);
92 | }
93 |
94 | /**
95 | * This checks the cache for an existing inventory. If it exists it is
96 | * returned. Otherwise a new inventory is created.
97 | *
98 | * @param steamId64 The 64bit Steam ID of the user
99 | * @param fetchNow Whether the data should be fetched now
100 | * @param bypassCache Whether the cache should be bypassed
101 | * @return The Team Fortress 2 Beta inventory for the given user
102 | * @throws SteamCondenserException if creating the inventory fails
103 | */
104 | public static TF2BetaInventory create(long steamId64, boolean fetchNow, boolean bypassCache)
105 | throws SteamCondenserException {
106 | return (TF2BetaInventory) create(APP_ID, steamId64, fetchNow, bypassCache);
107 | }
108 |
109 | /**
110 | * Creates a new inventory instance for the player with the given Steam ID
111 | *
112 | * @param steamId64 The 64bit Steam ID of the user
113 | * @param fetchNow Whether the data should be fetched now
114 | * @see GameInventory#create
115 | * @throws WebApiException on Web API errors
116 | */
117 | public TF2BetaInventory(long steamId64, boolean fetchNow)
118 | throws SteamCondenserException {
119 | super(APP_ID, steamId64, fetchNow);
120 | }
121 |
122 | /**
123 | * Returns the item class for Team Fortress 2
124 | *
125 | * @return The item class for Team Fortress 2 is TF2Item
126 | * @see TF2Item
127 | */
128 | protected Class extends GameItem> getItemClass() {
129 | return TF2Item.class;
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------