365 lines
18 KiB
Java
365 lines
18 KiB
Java
package com.alterdekim.game.component;
|
|
|
|
import com.alterdekim.game.component.game.*;
|
|
import com.alterdekim.game.component.game.friends.OneFR;
|
|
import com.alterdekim.game.component.game.friends.UserFriend;
|
|
import com.alterdekim.game.component.game.response.init.GetUserInfo;
|
|
import com.alterdekim.game.component.game.response.location.AddUserToLocation;
|
|
import com.alterdekim.game.component.rtmp.ConnectedProcessor;
|
|
import com.alterdekim.game.entity.PhoneMessage;
|
|
import com.alterdekim.game.entity.Postcard;
|
|
import com.alterdekim.game.message.*;
|
|
import com.alterdekim.game.message.amf.AMFMapper;
|
|
import com.alterdekim.game.message.amf.AMFObject;
|
|
import com.alterdekim.game.message.amf.AMFSerializer;
|
|
import com.alterdekim.game.message.amf.AMFValueType;
|
|
import com.alterdekim.game.security.AuthenticationUtil;
|
|
import com.alterdekim.game.service.*;
|
|
import com.alterdekim.game.utils.GameUtils;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
@Slf4j
|
|
@Component
|
|
public class GameServer {
|
|
|
|
@Autowired
|
|
private UserService users;
|
|
|
|
@Autowired
|
|
private LocationService locationService;
|
|
|
|
@Autowired
|
|
private RelationshipService relationshipService;
|
|
|
|
@Autowired
|
|
private PhoneMessageService phoneMessageService;
|
|
|
|
@Autowired
|
|
private PostcardService postcardService;
|
|
|
|
private final Map<Long, Player> players;
|
|
|
|
private final ObjectMapper xmlMapper;
|
|
|
|
public GameServer() {
|
|
this.players = new HashMap<>();
|
|
this.xmlMapper = new XmlMapper().registerModule(new JavaTimeModule());
|
|
}
|
|
|
|
public void onConnect(long playerId, String hwId, ConnectedProcessor connectedProcessor) {
|
|
log.warn("GameServer.onConnect() connect: {}", playerId);
|
|
Optional<String> username = users.getUsernameById(playerId);
|
|
if( username.isEmpty() || !AuthenticationUtil.hwIdAuth(users.findByUsername(username.get()), hwId) || this.players.containsKey(playerId) ) {
|
|
connectedProcessor.close();
|
|
return;
|
|
}
|
|
this.players.put(playerId, new Player(connectedProcessor, username.get()));
|
|
}
|
|
|
|
public void onMessage(long playerId, List<AMFObject> params ) {
|
|
try {
|
|
log.info("GameServer.onMessage() pid: {}, params: {}", playerId, params);
|
|
switch(CommandType.fromString( (String) params.get(0).getValue() )) {
|
|
case UserCommand:
|
|
processUserMessage(playerId, AMFMapper.deobfuscate(params.subList(1, params.size()), UserMessage.class));
|
|
break;
|
|
case SetLocation:
|
|
setPlayerLocation(playerId, AMFMapper.deobfuscate(params.subList(1, params.size()), SetLocationMessage.class));
|
|
break;
|
|
case SetUserAvatarState:
|
|
setPlayerAvatarState(playerId, AMFMapper.deobfuscate(params.subList(1, params.size()), SetPlayerAvatarState.class));
|
|
break;
|
|
case SetUserAvatarPosition:
|
|
setPlayerAvatarPosition(playerId, AMFMapper.deobfuscate(params.subList(1, params.size()), SetPlayerAvatarPosition.class));
|
|
break;
|
|
default:
|
|
log.warn("GameServer.onMessage() unknown command: {}", params.get(0));
|
|
}
|
|
} catch (Exception e) {
|
|
log.error("GameServer.onMessage() error: {}", e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
private void setPlayerAvatarPosition(long playerId, SetPlayerAvatarPosition message) {
|
|
players.get(playerId).setX((Double) message.getCoordinates().get("x").getValue());
|
|
players.get(playerId).setY((Double) message.getCoordinates().get("y").getValue());
|
|
this.sendInPlayersLocation(playerId, CommandType.SetUserAvatarPosition,
|
|
new SetPlayerPosition(
|
|
playerId,
|
|
new Point((Double) message.getCoordinates().get("x").getValue(), (Double) message.getCoordinates().get("y").getValue()),
|
|
message.getTweenerId().longValue()
|
|
)
|
|
);
|
|
}
|
|
|
|
private void setPlayerAvatarState(long playerId, SetPlayerAvatarState message) {
|
|
players.get(playerId).setState(message.getState());
|
|
this.sendInPlayersLocation(playerId, CommandType.SetUserAvatarState,
|
|
new SetPlayerState(playerId, message.getState())
|
|
);
|
|
}
|
|
|
|
private void processUserMessage(long playerId, UserMessage message) throws JsonProcessingException {
|
|
log.info("GameServer.processUserMessage() message: {}", message);
|
|
switch (UserCommandType.fromString(message.getCommandType())) {
|
|
case GetUserInitData -> {
|
|
String r = xmlMapper.writeValueAsString(users.getUserInitInfoByUserId(playerId));
|
|
this.sendResult(playerId, message.getTransactionId(), r);
|
|
}
|
|
case UserFriendsGet, GetOnlineUserFriends -> this.sendResult(playerId, message.getTransactionId(), users.getFriendsOfUser(playerId, players));
|
|
case UserFriendsRequests -> this.sendResult(playerId, message.getTransactionId(), users.getRequestsOfUser(playerId, players));
|
|
case RevokeUserFriendship -> {
|
|
users.removeFriendshipWithUser(playerId, Long.parseLong((String) message.getArguments().get(0).getValue()));
|
|
this.call(Long.parseLong((String) message.getArguments().get(0).getValue()), CommandType.OnFriendsRemoved, playerId);
|
|
}
|
|
case ApplyUserFriendship -> this.applyUserFriendship(playerId, (String) message.getArguments().get(0).getValue(), (String) message.getArguments().get(1).getValue());
|
|
case GetClubMap -> // todo: implement
|
|
this.sendResult(playerId, message.getTransactionId(), "</clubmap>");
|
|
case GetFriendPanelData -> this.sendResult(playerId, message.getTransactionId(), "");
|
|
case UpdateUserData -> this.sendResult(playerId, message.getTransactionId(), null);
|
|
case GetUserLocation -> {
|
|
double prevLocation = (Double) message.getArguments().get(2).getValue();
|
|
if (prevLocation == 0.0d) {
|
|
this.sendResult(playerId,
|
|
message.getTransactionId(),
|
|
xmlMapper.writeValueAsString(
|
|
locationService.getDefaultLocation()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
case ChatMessage -> {
|
|
String text = (String) message.getArguments().get(1).getValue();
|
|
sendInPlayersLocation(playerId, CommandType.ChatMessage, new ChatMessage(playerId, text));
|
|
}
|
|
case Shoot -> {
|
|
if (message.getSrv().equals("ROOM")) {
|
|
double x = (double) message.getArguments().get(1).getValue();
|
|
double y = (double) message.getArguments().get(2).getValue();
|
|
sendInPlayersLocation(playerId, CommandType.Shoot, new Shoot(playerId, x, y));
|
|
break;
|
|
}
|
|
this.sendInPlayersLocation(playerId, CommandType.SetUserAvatarState,
|
|
new SetPlayerState(playerId, players.get(playerId).getState())
|
|
);
|
|
this.users.setWeaponsCount(playerId, this.users.getIntegerUserProperty(playerId, PlayerProperties.WeaponsCount, 15) - 1);
|
|
}
|
|
case GetUserInfo -> getUserInfo(playerId, ((Double) message.getArguments().get(0).getValue()).longValue(), message);
|
|
case GetUserAvatar -> // todo: Implement avatar saving
|
|
getAvatar(playerId, message);
|
|
case GetSnInvitePanelData -> {}
|
|
case GetGrantBlocks -> {}
|
|
case UseMagicAbility -> this.sendInPlayersLocation(playerId, CommandType.UseMagicAbility, new UseMagicAbility(
|
|
Long.parseLong((String) message.getArguments().get(0).getValue()),
|
|
((Double) message.getArguments().get(1).getValue()).intValue()
|
|
));
|
|
case SetDefaultUserPhone -> {
|
|
long phoneId = ((Double) message.getArguments().get(0).getValue()).longValue();
|
|
this.users.setDefaultPhone(playerId, phoneId);
|
|
}
|
|
case SetUserBackground -> {
|
|
long bgId = ((Double) message.getArguments().get(0).getValue()).longValue();
|
|
this.users.setDefaultBackground(playerId, bgId);
|
|
}
|
|
case QueryUserFriendship -> {
|
|
long participantId = ((Double) message.getArguments().get(0).getValue()).longValue();
|
|
relationshipService.sendRequest(playerId, participantId);
|
|
this.call(participantId, CommandType.OnFriendshipRequestAdded,
|
|
new OneFR(
|
|
new UserFriend(playerId, players.get(playerId).getUsername(), players.get(playerId).getUsername(), true)
|
|
)
|
|
);
|
|
}
|
|
case GetPhoneBalance -> {
|
|
Integer balance = users.getUserAccountByUserId(playerId).getPhoneCardBalance();
|
|
this.sendResult(playerId, message.getTransactionId(), balance);
|
|
}
|
|
case PhoneMessage -> {
|
|
long receiverId = message.getArguments().get(0).getType() == AMFValueType.NUMBER ? ((Double) message.getArguments().get(0).getValue()).longValue() : Long.parseLong((String) message.getArguments().get(0).getValue());
|
|
if (receiverId <= 0) return;
|
|
String messageText = ((String) message.getArguments().get(1).getValue());
|
|
PhoneMessage pm = new PhoneMessage();
|
|
pm.setText(messageText);
|
|
pm.setSenderId(playerId);
|
|
pm.setReceiverId(receiverId);
|
|
pm.setIsNew(true);
|
|
pm.setSenderName(players.get(playerId).getUsername());
|
|
pm.setDateSent("");
|
|
phoneMessageService.savePhoneMessage(pm);
|
|
this.call(receiverId, CommandType.AddNewPhoneMessage, xmlMapper.writeValueAsString(pm));
|
|
}
|
|
case MarkPhoneMessageAsRead -> {
|
|
Long messageId = ((Double) message.getArguments().get(0).getValue()).longValue();
|
|
phoneMessageService.markAsRead(messageId);
|
|
}
|
|
case DeletePhoneMessage -> {
|
|
Long messageId1 = ((Double) message.getArguments().get(0).getValue()).longValue();
|
|
phoneMessageService.removeById(messageId1);
|
|
}
|
|
case CardMessage -> {
|
|
if( !subtractUsualTickets(playerId, 20) ) return;
|
|
long receiverId = ((Number) message.getArguments().get(0).getValue()).longValue();
|
|
int postcardId = ((Number) message.getArguments().get(1).getValue()).intValue();
|
|
Postcard postcard = new Postcard();
|
|
postcard.setIsNew(true);
|
|
postcard.setDateSent("");
|
|
postcard.setSenderId(playerId);
|
|
postcard.setReceiverId(receiverId);
|
|
postcard.setSenderName(players.get(playerId).getUsername());
|
|
postcard.setCardId(postcardId);
|
|
postcardService.savePostcard(postcard);
|
|
this.call(receiverId, CommandType.CardMessage, xmlMapper.writeValueAsString(postcard));
|
|
}
|
|
case MarkCardMessageAsRead -> {
|
|
long postcardMessageId = ((Number) message.getArguments().get(0).getValue()).longValue();
|
|
this.postcardService.markAsRead(postcardMessageId);
|
|
}
|
|
case DeleteCardMessage -> {
|
|
long postcardMessageId = ((Number) message.getArguments().get(0).getValue()).longValue();
|
|
this.postcardService.removeById(postcardMessageId);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean subtractUsualTickets(long userId, int amount) {
|
|
int val = this.users.getUsualTickets(userId);
|
|
if ( val >= amount ) {
|
|
val -= amount;
|
|
this.users.setUsualTickets(userId, val);
|
|
this.call(userId, CommandType.UpdateTickets, new UpdateTickets(val, this.users.getMagicTickets(userId)));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean subtractMagicTickets(long userId, int amount) {
|
|
int val = this.users.getMagicTickets(userId);
|
|
if ( val >= amount ) {
|
|
val -= amount;
|
|
this.users.setMagicTickets(userId, val);
|
|
this.call(userId, CommandType.UpdateTickets, new UpdateTickets(this.users.getUsualTickets(userId), val));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void applyUserFriendship(Long userId, String acceptString, String denyString) {
|
|
List<Long> accepted = Arrays.stream(acceptString.split(","))
|
|
.filter(s -> !s.isEmpty())
|
|
.map(Long::parseLong)
|
|
.collect(Collectors.toList());
|
|
List<Long> denied = Arrays.stream(denyString.split(","))
|
|
.filter(s -> !s.isEmpty())
|
|
.map(Long::parseLong)
|
|
.collect(Collectors.toList());
|
|
users.acceptFriendshipWithUsers(userId, accepted);
|
|
users.denyFriendshipsWithUsers(userId, denied);
|
|
Map<String, UserFriend> m = new HashMap<>();
|
|
m.put("1", new UserFriend(userId, "", players.get(userId).getUsername(), true));
|
|
accepted.forEach(uid -> this.call(uid, CommandType.OnFriendsAdded, m));
|
|
}
|
|
|
|
private void getAvatar(long playerId, UserMessage message) throws JsonProcessingException {
|
|
String r = xmlMapper.writeValueAsString(users.getUserAvatarByUserId(playerId));
|
|
r = r.substring(13);
|
|
r = r.substring(0, r.length() - 14);
|
|
log.info("getAvatar: {}", r);
|
|
this.sendResult(playerId, message.getTransactionId(), r);
|
|
}
|
|
|
|
private void getUserInfo(long playerId, long targetId, UserMessage message) throws JsonProcessingException {
|
|
String r = xmlMapper.writeValueAsString(new GetUserInfo(users.getUserInfoByUserId(targetId, false, playerId), users.getOtherUserAvatar(targetId)));
|
|
r = r.substring(11);
|
|
r = r.substring(0, r.length()-12);
|
|
this.sendResult(playerId, message.getTransactionId(), r);
|
|
}
|
|
|
|
private void setPlayerLocation(long playerId, SetLocationMessage message) {
|
|
Player p = this.players.get(playerId);
|
|
this.deleteSelf(playerId, p.getLocationId());
|
|
int prevLocation = p.getLocationId();
|
|
p.setLocationId(GameUtils.extractLocationId(message.getLocation()));
|
|
p.setX((Double) message.getCoordinates().get("x").getValue());
|
|
p.setY((Double) message.getCoordinates().get("y").getValue());
|
|
p.setState(message.getStartState());
|
|
this.updateLocationPlayers(playerId, prevLocation);
|
|
this.sendResult(playerId, message.getTransactionId(), "");
|
|
}
|
|
|
|
private void deleteSelf(long playerId, int locationId) {
|
|
this.sendInLocation(locationId, CommandType.RemoveUserFromLocation, playerId);
|
|
}
|
|
|
|
private void updateLocationPlayers(long playerId, int prevLocation) {
|
|
int locationId = this.players.get(playerId).getLocationId();
|
|
this.sendInLocation(prevLocation, CommandType.RemoveUserFromLocation, playerId);
|
|
for( int i = 0; i < 5; i++ ) {
|
|
this.sendInPlayersLocation(playerId, CommandType.AddUserToLocation, new AddUserToLocation(
|
|
playerId,
|
|
users.getAvatarById(playerId, this.players.get(playerId))
|
|
));
|
|
}
|
|
this.players.keySet().forEach(pid -> {
|
|
Player p = this.players.get(pid);
|
|
if( p.getLocationId() != locationId ) return;
|
|
this.call(playerId, CommandType.AddUserToLocation, new AddUserToLocation(
|
|
pid,
|
|
users.getAvatarById(pid, this.players.get(pid))
|
|
));
|
|
this.call(playerId, CommandType.SetUserAvatarState, new SetPlayerState(
|
|
pid,
|
|
this.players.get(pid).getState()
|
|
));
|
|
});
|
|
}
|
|
|
|
private void sendInPlayersLocation(long playerId, CommandType type, Object obj) {
|
|
this.sendInLocation(players.get(playerId).getLocationId(), type, obj);
|
|
}
|
|
|
|
private void sendInLocation(int locationId, CommandType type, Object obj) {
|
|
players.keySet().forEach(k -> {
|
|
if( locationId != players.get(k).getLocationId() ) return;
|
|
this.call(k, type, obj);
|
|
});
|
|
}
|
|
|
|
private void sendResult(long playerId, double tid, Object obj) {
|
|
this.sendMessage(playerId, new CallMessage("_result", tid, null, obj));
|
|
}
|
|
|
|
private void call(long playerId, CommandType function, Object obj) {
|
|
this.sendMessage(playerId, new CallMessage(function.getValue(), 0d, null, obj));
|
|
}
|
|
|
|
private void sendMessage(long playerId, Object obj) {
|
|
try {
|
|
this.players.get(playerId)
|
|
.getConnection()
|
|
.write_packet(AMFSerializer.serialize(obj));
|
|
} catch (IOException e) {
|
|
log.error("Unable to send message: {}", e.getMessage());
|
|
}
|
|
}
|
|
|
|
public void onDisconnect(long playerId) {
|
|
log.warn("GameServer.onDisconnect() close: {}", playerId);
|
|
if( !this.players.containsKey(playerId) ) return;
|
|
this.deleteSelf(playerId, this.players.get(playerId).getLocationId());
|
|
this.players.remove(playerId);
|
|
}
|
|
|
|
public int getPlayersCount() {
|
|
return this.players.size();
|
|
}
|
|
} |