Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Lorenzune
2026-03-16 15:17:18 +01:00
14 changed files with 528 additions and 63 deletions
@@ -0,0 +1,6 @@
ALTER TABLE `permissions` ADD `cmd_update_all` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `cmd_update_achievements`;
INSERT INTO `emulator_texts` (`key`, `value`) VALUES
('commands.keys.cmd_update_all', 'update_all'),
('commands.description.cmd_update_all', ':update_all'),
('commands.succes.cmd_update_all', 'Successfully updated everything!');
@@ -49,4 +49,20 @@ public class RoomUserPetComposer extends MessageComposer {
this.response.appendString("");
return this.response;
}
public int getPetType() {
return petType;
}
public int getRace() {
return race;
}
public String getColor() {
return color;
}
public Habbo getHabbo() {
return habbo;
}
}
@@ -15,7 +15,7 @@ public class AboutCommand extends Command {
}
public static String credits = "Arcturus Morningstar is an opensource project based on Arcturus By TheGeneral \n" +
"The Following people have all contributed to this emulator:\n" +
" TheGeneral\n Beny\n Alejandro\n Capheus\n Skeletor\n Harmonic\n Mike\n Remco\n zGrav \n Quadral \n Harmony\n Swirny\n ArpyAge\n Mikkel\n Rodolfo\n Rasmus\n Kitt Mustang\n Snaiker\n nttzx\n necmi\n Dome\n Jose Flores\n Cam\n Oliver\n Narzo\n Tenshie\n MartenM\n Ridge\n SenpaiDipper\n Snaiker\n Thijmen";
"TheGeneral\n Beny\n Alejandro\n Capheus\n Skeletor\n Harmonic\n Mike\n Remco\n zGrav \n Quadral \n Harmony\n Swirny\n ArpyAge\n Mikkel\n Rodolfo\n Rasmus\n Kitt Mustang\n Snaiker\n nttzx\n necmi\n Dome\n Jose Flores\n Cam\n Oliver\n Narzo\n Tenshie\n MartenM\n Ridge\n SenpaiDipper\n Snaiker\n Thijmen\n DuckieTM\n simoleo89\n Medievalshell\n Lorenzune";
@Override
public boolean handle(GameClient gameClient, String[] params) {
@@ -275,6 +275,7 @@ public class CommandHandler {
addCommand(new UnbanCommand());
addCommand(new UnloadRoomCommand());
addCommand(new UnmuteCommand());
addCommand(new UpdateAllCommand());
addCommand(new UpdateAchievements());
addCommand(new UpdateBotsCommand());
addCommand(new UpdateCalendarCommand());
@@ -0,0 +1,102 @@
package com.eu.habbo.habbohotel.commands;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
import com.eu.habbo.messages.outgoing.catalog.*;
import com.eu.habbo.messages.outgoing.catalog.marketplace.MarketplaceConfigComposer;
import com.eu.habbo.messages.outgoing.rooms.RoomRelativeMapComposer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UpdateAllCommand extends Command {
private static final Logger LOGGER = LoggerFactory.getLogger(UpdateAllCommand.class);
public UpdateAllCommand() {
super("cmd_update_all", Emulator.getTexts().getValue("commands.keys.cmd_update_all").split(";"));
}
@Override
public boolean handle(GameClient gameClient, String[] params) throws Exception {
LOGGER.info("[UpdateAll] Reloading all subsystems...");
// Achievements
Emulator.getGameEnvironment().getAchievementManager().reload();
// Bots
Emulator.getGameEnvironment().getBotManager().reload();
// Catalog
Emulator.getGameEnvironment().getCatalogManager().initialize();
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new CatalogUpdatedComposer());
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new CatalogModeComposer(0));
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new DiscountComposer());
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new MarketplaceConfigComposer());
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new GiftConfigurationComposer());
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new RecyclerLogicComposer());
// Crafting
Emulator.getGameEnvironment().getCraftingManager().reload();
// Config
Emulator.getConfig().reload();
// Guild Parts
Emulator.getGameEnvironment().getGuildManager().loadGuildParts();
Emulator.getBadgeImager().reload();
// Hotel View
Emulator.getGameEnvironment().getHotelViewManager().getNewsList().reload();
Emulator.getGameEnvironment().getHotelViewManager().getHallOfFame().reload();
// Items
Emulator.getGameEnvironment().getItemManager().loadItems();
Emulator.getGameEnvironment().getItemManager().loadCrackable();
Emulator.getGameEnvironment().getItemManager().loadSoundTracks();
synchronized (Emulator.getGameEnvironment().getRoomManager().getActiveRooms()) {
for (Room room : Emulator.getGameEnvironment().getRoomManager().getActiveRooms()) {
if (room.isLoaded() && room.getUserCount() > 0 && room.getLayout() != null) {
room.sendComposer(new RoomRelativeMapComposer(room).compose());
}
}
}
// Navigator
Emulator.getGameEnvironment().getNavigatorManager().loadNavigator();
// Room Models
Emulator.getGameEnvironment().getRoomManager().loadRoomModels();
Emulator.getGameEnvironment().getRoomManager().loadPublicRooms();
// Permissions
Emulator.getGameEnvironment().getPermissionsManager().reload();
// Pet Data
Emulator.getGameEnvironment().getPetManager().reloadPetData();
// Polls
Emulator.getGameEnvironment().getPollManager().loadPolls();
// Texts & Commands
try {
Emulator.getTexts().reload();
Emulator.getGameEnvironment().getCommandHandler().reloadCommands();
} catch (Exception e) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_update_texts.failed"), RoomChatMessageBubbles.ALERT);
}
// Word Filter
Emulator.getGameEnvironment().getWordFilter().reload();
// YouTube
Emulator.getGameEnvironment().getItemManager().getYoutubeManager().load();
LOGGER.info("[UpdateAll] All subsystems reloaded successfully!");
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_update_all"), RoomChatMessageBubbles.ALERT);
return true;
}
}
@@ -17,6 +17,7 @@ import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.scoreboards
import com.eu.habbo.habbohotel.items.interactions.games.battlebanzai.scoreboards.InteractionBattleBanzaiScoreboardYellow;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionFootball;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionFootballGate;
import com.eu.habbo.habbohotel.items.interactions.games.football.InteractionRebugFootball;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoalBlue;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoalGreen;
import com.eu.habbo.habbohotel.items.interactions.games.football.goals.InteractionFootballGoalRed;
@@ -345,6 +346,7 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("football", InteractionFootball.class));
this.interactionsList.add(new ItemInteraction("rebug_football", InteractionRebugFootball.class));
this.interactionsList.add(new ItemInteraction("football_gate", InteractionFootballGate.class));
this.interactionsList.add(new ItemInteraction("football_counter_blue", InteractionFootballScoreboardBlue.class));
this.interactionsList.add(new ItemInteraction("football_counter_green", InteractionFootballScoreboardGreen.class));
@@ -0,0 +1,54 @@
package com.eu.habbo.habbohotel.items.interactions.games.football;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionDefault;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.threading.runnables.RebugKickBallAction;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Rebug-style football interaction.
* Uses simplified momentum-decay physics with 180-degree bounce.
* Set interaction_type to "rebug_football" on a ball item to use this instead of the default football physics.
*/
public class InteractionRebugFootball extends InteractionDefault {
private RebugKickBallAction currentThread;
public InteractionRebugFootball(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.setExtradata("0");
}
public InteractionRebugFootball(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.setExtradata("0");
}
@Override
public boolean canWalkOn(RoomUnit roomUnit, Room room, Object[] objects) {
return true;
}
@Override
public boolean isWalkable() {
return true;
}
@Override
public void onWalkOn(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
super.onWalkOn(roomUnit, room, objects);
if (this.currentThread != null) {
this.currentThread.dead = true;
}
boolean hasPath = !roomUnit.getPath().isEmpty();
this.currentThread = new RebugKickBallAction(this, room, roomUnit, hasPath);
Emulator.getThreading().run(this.currentThread, 50);
}
}
@@ -435,11 +435,11 @@ public class RoomCycleManager {
if (!unit.isWalking() && !unit.cmdSit) {
// Don't override special pet statuses with SIT
boolean hasSpecialPetStatus = unit.hasStatus(RoomUnitStatus.HANG)
|| unit.hasStatus(RoomUnitStatus.SWING)
|| unit.hasStatus(RoomUnitStatus.FLAME)
|| unit.hasStatus(RoomUnitStatus.PLAY);
boolean hasSpecialPetStatus = unit.hasStatus(RoomUnitStatus.HANG)
|| unit.hasStatus(RoomUnitStatus.SWING)
|| unit.hasStatus(RoomUnitStatus.FLAME)
|| unit.hasStatus(RoomUnitStatus.PLAY);
RoomTile thisTile = this.room.getLayout().getTile(unit.getX(), unit.getY());
HabboItem topItem = this.room.getTallestChair(thisTile);
@@ -473,12 +473,16 @@ public class RoomCycleManager {
BedProfile bedProfile = new BedProfile(topItem);
double layHeight = Item.getCurrentHeight(topItem) * 1.0D + bedProfile.getLayZOffset();
LOGGER.info("[BedProfile] item={} stackHeight={} isFlat={} isDouble={} X={} Y={} Z={}",
topItem.getBaseItem().getName(), topItem.getBaseItem().getHeight(),
bedProfile.isFlat(), bedProfile.isDouble(),
bedProfile.getLayXOffset(), bedProfile.getLayYOffset(), bedProfile.getLayZOffset());
topItem.getBaseItem().getName(), topItem.getBaseItem().getHeight(),
bedProfile.isFlat(), bedProfile.isDouble(),
bedProfile.getLayXOffset(), bedProfile.getLayYOffset(), bedProfile.getLayZOffset());
unit.setStatus(RoomUnitStatus.LAY, layHeight + ";" + bedProfile.getLayXOffset() + ";" + bedProfile.getLayYOffset());
unit.setRotation(RoomUserRotation.values()[topItem.getRotation() % 4]);
unit.setLocation(bedProfile.snapToLay(this.room, topItem, unit.getX(), unit.getY()));
// Check love effect when a user enters a bed
this.room.getUnitManager().checkBedLoveEffect(topItem);
update = true;
}
}
@@ -10,17 +10,13 @@ import com.eu.habbo.habbohotel.pets.PetVocalsType;
import com.eu.habbo.habbohotel.pets.RideablePet;
import com.eu.habbo.habbohotel.users.DanceType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboGender;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.outgoing.generic.alerts.GenericErrorMessagesComposer;
import com.eu.habbo.messages.outgoing.inventory.AddPetComposer;
import com.eu.habbo.messages.outgoing.rooms.pets.RoomPetComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUnitIdleComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDanceComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserEffectComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserHandItemComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserRemoveComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.outgoing.rooms.users.*;
import gnu.trove.TCollections;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.map.TIntObjectMap;
@@ -47,6 +43,7 @@ import java.util.stream.Collectors;
*/
public class RoomUnitManager {
private static final Logger LOGGER = LoggerFactory.getLogger(RoomUnitManager.class);
static final int BED_LOVE_EFFECT_ID = 9;
private final Room room;
@@ -269,14 +266,14 @@ public class RoomUnitManager {
public void kickHabbo(Habbo habbo, boolean alert) {
if (alert) {
habbo.getClient().sendResponse(
new GenericErrorMessagesComposer(GenericErrorMessagesComposer.KICKED_OUT_OF_THE_ROOM));
new GenericErrorMessagesComposer(GenericErrorMessagesComposer.KICKED_OUT_OF_THE_ROOM));
}
habbo.getRoomUnit().isKicked = true;
habbo.getRoomUnit().setGoalLocation(this.room.getLayout().getDoorTile());
if (habbo.getRoomUnit().getPath() == null || habbo.getRoomUnit().getPath().size() <= 1
|| this.room.isPublicRoom()) {
|| this.room.isPublicRoom()) {
habbo.getRoomUnit().setCanWalk(true);
Emulator.getGameEnvironment().getRoomManager().leaveRoom(habbo, this.room);
}
@@ -356,8 +353,8 @@ public class RoomUnitManager {
double z = habbo.getRoomUnit().getCurrentLocation().getStackHeight();
if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT)
|| (topItem != null && topItem.getBaseItem().allowSit())) {
if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT)
|| (topItem != null && topItem.getBaseItem().allowSit())) {
if (topItem != null && topItem.getBaseItem().allowSit()) {
if (!habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT)) {
this.dance(habbo, DanceType.NONE);
@@ -365,8 +362,8 @@ public class RoomUnitManager {
habbo.getRoomUnit().setZ(topItem.getZ());
habbo.getRoomUnit().setPreviousLocationZ(topItem.getZ());
habbo.getRoomUnit().setRotation(RoomUserRotation.fromValue(topItem.getRotation()));
habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT,
String.valueOf(Item.getCurrentHeight(topItem)));
habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT,
String.valueOf(Item.getCurrentHeight(topItem)));
habbo.getRoomUnit().cmdSit = false;
} else if (habbo.getRoomUnit().cmdSit) {
habbo.getRoomUnit().setZ(z - 0.5);
@@ -377,10 +374,31 @@ public class RoomUnitManager {
habbo.getRoomUnit().setPreviousLocationZ(z);
}
} else if (topItem != null && topItem.getBaseItem().allowLay()) {
BedProfile bedProfile = new BedProfile(topItem);
// Snap user to the correct pillow tile for the current bed rotation
RoomTile pillowTile = bedProfile.snapToLay(this.room, topItem, habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY());
// For double beds: if another user already occupies this pillow, use the other side
if (pillowTile != null && bedProfile.isDouble()) {
THashSet<Habbo> habbosAtPillow = this.getHabbosAt(pillowTile.x, pillowTile.y);
for (Habbo other : habbosAtPillow) {
if (other == habbo || other.getRoomUnit() == null) continue;
RoomTile otherSide = bedProfile.getOtherSide(this.room, topItem, pillowTile);
if (otherSide != null) {
pillowTile = otherSide;
}
break;
}
}
if (pillowTile != null) {
habbo.getRoomUnit().setLocation(pillowTile);
}
habbo.getRoomUnit().setZ(topItem.getZ());
habbo.getRoomUnit().setPreviousLocationZ(topItem.getZ());
habbo.getRoomUnit().setRotation(RoomUserRotation.fromValue(topItem.getRotation() % 4));
BedProfile bedProfile = new BedProfile(topItem);
double layHeight = Item.getCurrentHeight(topItem) + bedProfile.getLayZOffset();
habbo.getRoomUnit().setStatus(RoomUnitStatus.LAY, layHeight + ";" + bedProfile.getLayXOffset() + ";" + bedProfile.getLayYOffset());
} else {
@@ -404,6 +422,10 @@ public class RoomUnitManager {
}
this.room.sendComposer(new RoomUserStatusComposer(roomUnits, true).compose());
}
if (topItem != null && topItem.getBaseItem().allowLay()) {
this.checkBedLoveEffect(topItem);
}
}
// ==================== HABBO QUEUE ====================
@@ -449,7 +471,7 @@ public class RoomUnitManager {
this.currentBots.clear();
try (PreparedStatement statement = connection.prepareStatement(
"SELECT users.username AS owner_name, bots.* FROM bots INNER JOIN users ON bots.user_id = users.id WHERE room_id = ?")) {
"SELECT users.username AS owner_name, bots.* FROM bots INNER JOIN users ON bots.user_id = users.id WHERE room_id = ?")) {
statement.setInt(1, this.room.getId());
try (ResultSet set = statement.executeQuery()) {
while (set.next()) {
@@ -463,13 +485,13 @@ public class RoomUnitManager {
bot.getRoomUnit().setHeadRotation(RoomUserRotation.fromValue(set.getInt("rot")));
bot.getRoomUnit().setDanceType(DanceType.values()[set.getInt("dance")]);
bot.getRoomUnit().setLocation(this.room.getLayout().getTile(
(short) set.getInt("x"), (short) set.getInt("y")));
(short) set.getInt("x"), (short) set.getInt("y")));
bot.getRoomUnit().setZ(set.getDouble("z"));
bot.getRoomUnit().setPreviousLocationZ(set.getDouble("z"));
bot.getRoomUnit().setPathFinderRoom(this.room);
bot.getRoomUnit().setCanWalk(set.getBoolean("freeroam"));
this.addBot(bot);
if (!this.room.getFurniOwnerNames().containsKey(bot.getOwnerId())) {
this.room.getFurniOwnerNames().put(bot.getOwnerId(), set.getString("owner_name"));
}
@@ -693,8 +715,8 @@ public class RoomUnitManager {
bot.getRoomUnit().setZ(topItem.getZ());
bot.getRoomUnit().setPreviousLocationZ(topItem.getZ());
bot.getRoomUnit().setRotation(RoomUserRotation.fromValue(topItem.getRotation()));
bot.getRoomUnit().setStatus(RoomUnitStatus.SIT,
String.valueOf(Item.getCurrentHeight(topItem)));
bot.getRoomUnit().setStatus(RoomUnitStatus.SIT,
String.valueOf(Item.getCurrentHeight(topItem)));
} else if (topItem != null && topItem.getBaseItem().allowLay()) {
bot.getRoomUnit().setZ(topItem.getZ());
bot.getRoomUnit().setPreviousLocationZ(topItem.getZ());
@@ -717,8 +739,8 @@ public class RoomUnitManager {
if (!bots.isEmpty()) {
this.room.sendComposer(new RoomUserStatusComposer(
bots.stream().map(Bot::getRoomUnit).collect(Collectors.toCollection(THashSet::new)),
true).compose());
bots.stream().map(Bot::getRoomUnit).collect(Collectors.toCollection(THashSet::new)),
true).compose());
}
}
@@ -731,7 +753,7 @@ public class RoomUnitManager {
this.currentPets.clear();
try (PreparedStatement statement = connection.prepareStatement(
"SELECT users.username as pet_owner_name, users_pets.* FROM users_pets INNER JOIN users ON users_pets.user_id = users.id WHERE room_id = ?")) {
"SELECT users.username as pet_owner_name, users_pets.* FROM users_pets INNER JOIN users ON users_pets.user_id = users.id WHERE room_id = ?")) {
statement.setInt(1, this.room.getId());
try (ResultSet set = statement.executeQuery()) {
while (set.next()) {
@@ -742,13 +764,13 @@ public class RoomUnitManager {
pet.getRoomUnit().setBodyRotation(RoomUserRotation.fromValue(set.getInt("rot")));
pet.getRoomUnit().setHeadRotation(RoomUserRotation.fromValue(set.getInt("rot")));
pet.getRoomUnit().setLocation(this.room.getLayout().getTile(
(short) set.getInt("x"), (short) set.getInt("y")));
(short) set.getInt("x"), (short) set.getInt("y")));
pet.getRoomUnit().setZ(set.getDouble("z"));
pet.getRoomUnit().setPreviousLocationZ(set.getDouble("z"));
pet.getRoomUnit().setPathFinderRoom(this.room);
pet.getRoomUnit().setCanWalk(true);
this.addPet(pet);
if (!this.room.getFurniOwnerNames().containsKey(pet.getUserId())) {
this.room.getFurniOwnerNames().put(pet.getUserId(), set.getString("pet_owner_name"));
}
@@ -814,7 +836,7 @@ public class RoomUnitManager {
Habbo habbo = this.getHabbo(pet.getUserId());
if (habbo != null) {
this.room.getFurniOwnerNames().put(pet.getUserId(),
this.getHabbo(pet.getUserId()).getHabboInfo().getUsername());
this.getHabbo(pet.getUserId()).getHabboInfo().getUsername());
}
}
}
@@ -849,17 +871,17 @@ public class RoomUnitManager {
if (pet.getRoomUnit().getCurrentLocation() == null) {
pet.getRoomUnit().setLocation(this.room.getLayout().getDoorTile());
pet.getRoomUnit().setRotation(RoomUserRotation.fromValue(
this.room.getLayout().getDoorDirection()));
this.room.getLayout().getDoorDirection()));
}
pet.needsUpdate = true;
Habbo owner = this.getHabbo(pet.getUserId());
if (owner != null) {
this.room.getFurniOwnerNames().put(pet.getUserId(),
owner.getHabboInfo().getUsername());
owner.getHabboInfo().getUsername());
}
this.addPet(pet);
this.room.sendComposer(new RoomPetComposer(pet).compose());
}
@@ -881,7 +903,7 @@ public class RoomUnitManager {
}
if (petIterator.value().getRoomUnit().getX() == x
&& petIterator.value().getRoomUnit().getY() == y) {
&& petIterator.value().getRoomUnit().getY() == y) {
return true;
}
}
@@ -950,8 +972,8 @@ public class RoomUnitManager {
if (!pets.isEmpty()) {
this.room.sendComposer(new RoomUserStatusComposer(
pets.stream().map(Pet::getRoomUnit).collect(Collectors.toCollection(THashSet::new)),
true).compose());
pets.stream().map(Pet::getRoomUnit).collect(Collectors.toCollection(THashSet::new)),
true).compose());
}
}
@@ -981,11 +1003,11 @@ public class RoomUnitManager {
for (Pet pet : pets) {
pet.setRoom(null);
pet.needsUpdate = true;
if (pet instanceof RideablePet) {
((RideablePet) pet).setRider(null);
}
pet.run(); // Run synchronously to ensure DB is updated before returning pet to inventory
habbo.getInventory().getPetsComponent().addPet(pet);
habbo.getClient().sendResponse(new AddPetComposer(pet));
@@ -1028,19 +1050,19 @@ public class RoomUnitManager {
for (Pet pet : toRemove) {
pet.setRoom(null);
pet.needsUpdate = true;
if (pet instanceof RideablePet) {
((RideablePet) pet).setRider(null);
}
pet.run(); // Run synchronously to ensure DB is updated before room reload
Habbo owner = Emulator.getGameEnvironment().getHabboManager().getHabbo(pet.getUserId());
if (owner != null) {
owner.getInventory().getPetsComponent().addPet(pet);
owner.getClient().sendResponse(new AddPetComposer(pet));
}
this.currentPets.remove(pet.getId());
this.room.sendComposer(new RoomUserRemoveComposer(pet.getRoomUnit()).compose());
}
@@ -1087,24 +1109,24 @@ public class RoomUnitManager {
for (Habbo habbo : this.currentHabbos.values()) {
if (habbo != null && habbo.getRoomUnit() != null && habbo.getRoomUnit().getRoom() != null
&& habbo.getRoomUnit().getRoom().getId() == this.room.getId() && (atTile == null
|| habbo.getRoomUnit().getCurrentLocation() == atTile)) {
&& habbo.getRoomUnit().getRoom().getId() == this.room.getId() && (atTile == null
|| habbo.getRoomUnit().getCurrentLocation() == atTile)) {
units.add(habbo.getRoomUnit());
}
}
for (Pet pet : this.currentPets.valueCollection()) {
if (pet != null && pet.getRoomUnit() != null && pet.getRoomUnit().getRoom() != null
&& pet.getRoomUnit().getRoom().getId() == this.room.getId() && (atTile == null
|| pet.getRoomUnit().getCurrentLocation() == atTile)) {
&& pet.getRoomUnit().getRoom().getId() == this.room.getId() && (atTile == null
|| pet.getRoomUnit().getCurrentLocation() == atTile)) {
units.add(pet.getRoomUnit());
}
}
for (Bot bot : this.currentBots.valueCollection()) {
if (bot != null && bot.getRoomUnit() != null && bot.getRoomUnit().getRoom() != null
&& bot.getRoomUnit().getRoom().getId() == this.room.getId() && (atTile == null
|| bot.getRoomUnit().getCurrentLocation() == atTile)) {
&& bot.getRoomUnit().getRoom().getId() == this.room.getId() && (atTile == null
|| bot.getRoomUnit().getCurrentLocation() == atTile)) {
units.add(bot.getRoomUnit());
}
}
@@ -1118,7 +1140,7 @@ public class RoomUnitManager {
public Collection<RoomUnit> getRoomUnitsAt(RoomTile tile) {
THashSet<RoomUnit> roomUnits = getRoomUnits();
return roomUnits.stream().filter(unit -> unit.getCurrentLocation().equals(tile))
.collect(Collectors.toSet());
.collect(Collectors.toSet());
}
// ==================== EFFECTS AND HAND ITEMS ====================
@@ -1148,6 +1170,31 @@ public class RoomUnitManager {
}
}
public void checkBedLoveEffect(HabboItem bed) {
if (bed == null || !bed.getBaseItem().allowLay()) return;
BedProfile bedProfile = new BedProfile(bed);
if (!bedProfile.isDouble()) return;
THashSet<Habbo> habbosOnBed = this.getHabbosOnItem(bed);
Habbo male = null;
Habbo female = null;
for (Habbo h : habbosOnBed) {
if (h.getRoomUnit() == null || !h.getRoomUnit().hasStatus(RoomUnitStatus.LAY)) continue;
if (h.getHabboInfo().getGender() == HabboGender.M && male == null) {
male = h;
} else if (h.getHabboInfo().getGender() == HabboGender.F && female == null) {
female = h;
}
}
if (male != null && female != null) {
this.giveEffect(male.getRoomUnit(), BED_LOVE_EFFECT_ID, 5);
this.giveEffect(female.getRoomUnit(), BED_LOVE_EFFECT_ID, 5);
}
}
/**
* Gives a hand item to a Habbo.
*/
@@ -1222,7 +1269,7 @@ public class RoomUnitManager {
*/
public void teleportHabboToItem(Habbo habbo, HabboItem item) {
this.teleportRoomUnitToLocation(habbo.getRoomUnit(), item.getX(), item.getY(),
item.getZ() + Item.getCurrentHeight(item));
item.getZ() + Item.getCurrentHeight(item));
}
/**
@@ -1237,7 +1284,7 @@ public class RoomUnitManager {
*/
public void teleportRoomUnitToItem(RoomUnit roomUnit, HabboItem item) {
this.teleportRoomUnitToLocation(roomUnit, item.getX(), item.getY(),
item.getZ() + Item.getCurrentHeight(item));
item.getZ() + Item.getCurrentHeight(item));
}
/**
@@ -1314,9 +1361,9 @@ public class RoomUnitManager {
}
HabboItem doorTileTopItem = this.room.getTopItemAt(habbo.getRoomUnit().getX(),
habbo.getRoomUnit().getY());
if (doorTileTopItem != null
&& !(doorTileTopItem instanceof com.eu.habbo.habbohotel.items.interactions.InteractionTeleportTile)) {
habbo.getRoomUnit().getY());
if (doorTileTopItem != null
&& !(doorTileTopItem instanceof com.eu.habbo.habbohotel.items.interactions.InteractionTeleportTile)) {
try {
doorTileTopItem.onWalkOn(habbo.getRoomUnit(), this.room, new Object[]{});
} catch (Exception e) {
@@ -1342,8 +1389,8 @@ public class RoomUnitManager {
this.dance(habbo, DanceType.NONE);
habbo.getRoomUnit().cmdSit = true;
habbo.getRoomUnit().setBodyRotation(
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + "");
this.room.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
}
@@ -1360,8 +1407,8 @@ public class RoomUnitManager {
if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) {
habbo.getRoomUnit().cmdStand = true;
habbo.getRoomUnit().setBodyRotation(
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT);
this.room.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
}
@@ -0,0 +1,31 @@
package com.eu.habbo.messages.rcon;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.messages.outgoing.rooms.RoomRelativeMapComposer;
import com.google.gson.Gson;
public class UpdateItems extends RCONMessage<UpdateItems.JSONUpdateItems> {
public UpdateItems() {
super(JSONUpdateItems.class);
}
@Override
public void handle(Gson gson, JSONUpdateItems json) {
Emulator.getGameEnvironment().getItemManager().loadItems();
Emulator.getGameEnvironment().getItemManager().loadCrackable();
Emulator.getGameEnvironment().getItemManager().loadSoundTracks();
synchronized (Emulator.getGameEnvironment().getRoomManager().getActiveRooms()) {
for (Room room : Emulator.getGameEnvironment().getRoomManager().getActiveRooms()) {
if (room.isLoaded() && room.getUserCount() > 0 && room.getLayout() != null) {
room.sendComposer(new RoomRelativeMapComposer(room).compose());
}
}
}
}
static class JSONUpdateItems {
}
}
@@ -64,6 +64,7 @@ public class RCONServer extends Server {
this.addRCONMessage("giveuserclothing", GiveUserClothing.class);
this.addRCONMessage("modifysubscription", ModifyUserSubscription.class);
this.addRCONMessage("changeusername", ChangeUsername.class);
this.addRCONMessage("updateitems", UpdateItems.class);
Collections.addAll(this.allowedAdresses, Emulator.getConfig().getValue("rcon.allowed", "127.0.0.1").split(";"));
}
@@ -0,0 +1,106 @@
package com.eu.habbo.threading.runnables;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomTileState;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer;
import com.eu.habbo.util.pathfinding.Direction8;
import gnu.trove.set.hash.THashSet;
/**
* Alternative football physics based on the Rebug plugin.
* Uses momentum decay (ball slows down over time) and simple 180-degree bounce.
*/
public class RebugKickBallAction implements Runnable {
private final HabboItem ball;
private final Room room;
private Direction8 direction;
private int momentum;
public boolean dead = false;
public RebugKickBallAction(HabboItem ball, Room room, RoomUnit kicker, boolean hasPath) {
this.ball = ball;
this.room = room;
this.direction = Direction8.fromDelta(
ball.getX() - kicker.getX(),
ball.getY() - kicker.getY()
);
this.momentum = hasPath ? 55 : 0;
}
private boolean isTileBlocked(int x, int y) {
RoomTile tile = this.room.getLayout().getTile((short) x, (short) y);
if (tile == null) return true;
if (tile.hasUnits()) return true;
return tile.getState() != RoomTileState.OPEN;
}
@Override
public void run() {
if (this.dead || !this.room.isLoaded()) return;
try {
int nextX = this.ball.getX() + this.direction.getDiffX();
int nextY = this.ball.getY() + this.direction.getDiffY();
if (isTileBlocked(nextX, nextY)) {
this.direction = this.direction.rotateDirection180Degrees();
nextX = this.ball.getX() + this.direction.getDiffX();
nextY = this.ball.getY() + this.direction.getDiffY();
}
RoomTile nextTile = this.room.getLayout().getTile((short) nextX, (short) nextY);
if (nextTile == null) return;
RoomTile oldTile = this.room.getLayout().getTile(this.ball.getX(), this.ball.getY());
double oldZ = this.ball.getZ();
this.ball.setRotation(this.direction.getRot());
this.ball.setX(nextTile.x);
this.ball.setY(nextTile.y);
this.ball.setZ(nextTile.getStackHeight());
this.ball.needsUpdate(true);
// Schedule next movement based on momentum
long delay = getDelayForMomentum(this.momentum);
if (delay > 0) {
Emulator.getThreading().run(this, delay);
}
// Update tiles
this.room.updateTile(oldTile);
this.room.updateTile(nextTile);
THashSet<HabboItem> oldItems = this.room.getItemsAt(oldTile);
if (oldItems != null && !oldItems.isEmpty()) {
oldItems.remove(this.ball);
}
this.room.getItemsAt(nextTile).add(this.ball);
// Send rolling animation
this.room.sendComposer(new FloorItemOnRollerComposer(
this.ball, null, oldTile, oldZ, nextTile, this.ball.getZ(), 0.0D, this.room
).compose());
// Decay momentum
this.momentum -= 11;
} catch (Exception e) {
this.dead = true;
}
}
private long getDelayForMomentum(int momentum) {
switch (momentum) {
case 55: return 100L;
case 44: return 100L;
case 33: return 200L;
case 22: return 250L;
case 11: return 500L;
default: return 0L;
}
}
}
@@ -0,0 +1,95 @@
package com.eu.habbo.util.pathfinding;
/**
* 8-directional movement utility for ball physics.
* Ported from the Rebug soccer plugin.
*/
public class Direction8 {
public static final Direction8[] DIRECTIONS = new Direction8[8];
public static final Direction8 N = new Direction8(0, "N", 0, -1);
public static final Direction8 NE = new Direction8(1, "NE", 1, -1);
public static final Direction8 E = new Direction8(2, "E", 1, 0);
public static final Direction8 SE = new Direction8(3, "SE", 1, 1);
public static final Direction8 S = new Direction8(4, "S", 0, 1);
public static final Direction8 SW = new Direction8(5, "SW", -1, 1);
public static final Direction8 W = new Direction8(6, "W", -1, 0);
public static final Direction8 NW = new Direction8(7, "NW", -1, -1);
private final int rot;
private final String rotName;
private final int xDiff;
private final int yDiff;
public Direction8(int rot, String rotName, int diffX, int diffY) {
this.rot = rot;
this.rotName = rotName;
this.xDiff = diffX;
this.yDiff = diffY;
DIRECTIONS[rot] = this;
}
public static Direction8 fromDelta(int deltaX, int deltaY) {
if (deltaX == 0) {
if (deltaY < 0) return N;
if (deltaY > 0) return S;
}
if (deltaX > 0) {
if (deltaY < 0) return NE;
if (deltaY == 0) return E;
if (deltaY > 0) return SE;
}
if (deltaX < 0) {
if (deltaY < 0) return NW;
if (deltaY == 0) return W;
if (deltaY > 0) return SW;
}
return N;
}
public static Direction8 getDirection(int dir) {
if (dir < 0 || dir > 7) return N;
return DIRECTIONS[dir];
}
public static int validateDirection8Value(int dir) {
return dir & 0x7;
}
public int getRot() {
return this.rot;
}
public String getRotName() {
return this.rotName;
}
public int getDiffX() {
return this.xDiff;
}
public int getDiffY() {
return this.yDiff;
}
public Direction8 rotateDirection180Degrees() {
return getDirectionAtRot(4);
}
public Direction8 rotateDirection90Degrees(boolean clockwise) {
return getDirectionAtRot(clockwise ? 2 : -2);
}
public Direction8 rotateDirection45Degrees(boolean clockwise) {
return getDirectionAtRot(clockwise ? 1 : -1);
}
public Direction8 getDirectionAtRot(int diff) {
return DIRECTIONS[validateDirection8Value(this.rot + diff)];
}
@Override
public String toString() {
return this.rotName + "(" + this.rot + ")";
}
}