Commit 27ac8ada authored by RabidSquabbit's avatar RabidSquabbit

Xbox-style notifications

parent 396d6bd9
......@@ -237,6 +237,8 @@ add_library(ElDorito SHARED
src/Server/DedicatedServer.hpp
src/Server/BanList.cpp
src/Server/BanList.hpp
src/Server/HaloStats.cpp
src/Server/HaloStats.hpp
src/Server/ServerChat.cpp
src/Server/ServerChat.hpp
src/Server/VariableSynchronization.cpp
......
#include "HaloStats.hpp"
#include <d3dx9core.h>
#include <algorithm>
#include "../Patches/Network.hpp"
#include "../Blam/BlamNetwork.hpp"
#include "../Web/Ui/ScreenLayer.hpp"
#include "../Patches/CustomPackets.hpp"
#include "../Modules/ModuleServer.hpp"
#include "../Modules/ModulePlayer.hpp"
#include "../ThirdParty/rapidjson/writer.h"
#include "../ThirdParty/rapidjson/stringbuffer.h"
#include "../ElDorito.hpp"
#include "../Server/ServerChat.hpp"
#include "../ThirdParty/HttpRequest.hpp"
#include <iomanip>
#include <unordered_map>
namespace Server
{
namespace HaloStatsPackets
{
void ReceivedHaloStatsMessage(Blam::Network::Session *session, int peer, const HaloStatsMessage &message);
typedef Patches::CustomPackets::Packet<HaloStatsMessage> HaloStatsMessagePacket;
typedef Patches::CustomPackets::PacketSender<HaloStatsMessage> HaloStatsMessagePacketSender;
std::shared_ptr<HaloStatsMessagePacketSender> HaloStatsPacketSender;
// Packet handler for HaloStats messages
class HaloStatsMessagePacketHandler : public Patches::CustomPackets::PacketHandler<HaloStatsMessage>
{
public:
void Serialize(Blam::BitStream *stream, const HaloStatsMessage *data) override
{
// Message type
stream->WriteUnsigned(static_cast<uint32_t>(data->Type), 0U, static_cast<uint32_t>(HaloStatsMessageType::Count));
if (data->Type == HaloStatsMessageType::Rank)
stream->WriteUnsigned<uint8_t>(data->rank, 0, 49);
}
bool Deserialize(Blam::BitStream *stream, HaloStatsMessage *data) override
{
memset(data, 0, sizeof(*data));
// Message type
data->Type = static_cast<HaloStatsMessageType>(stream->ReadUnsigned(0U, static_cast<uint32_t>(HaloStatsMessageType::Count)));
if (static_cast<uint32_t>(data->Type) >= static_cast<uint32_t>(HaloStatsMessageType::Count))
return false;
if (data->Type == HaloStatsMessageType::Rank)
{
data->rank = stream->ReadUnsigned<uint8_t>(0, 49);
}
return true;
}
void HandlePacket(Blam::Network::ObserverChannel *sender, const HaloStatsMessagePacket *packet) override
{
auto session = Blam::Network::GetActiveSession();
if (!session)
return;
auto peer = session->GetChannelPeer(sender);
//if this packet didnt come from the host, ignore it
if (peer < 0 || peer != session->MembershipInfo.HostPeerIndex)
return;
ReceivedHaloStatsMessage(session, peer, packet->Data);
}
};
// Registered message handlers
std::vector<std::shared_ptr<HaloStatsMessageHandler>> haloStatsMessageHandlers;
// Callback for when a message is received.
void ReceivedHaloStatsMessage(Blam::Network::Session *session, int peer, const HaloStatsMessage &message)
{
// Send the message out to handlers
for (auto &&handler : haloStatsMessageHandlers)
handler->MessageReceived(message);
}
// Sends a HaloStats message to a peer as a packet.
bool SendHaloStatsPacket(const HaloStatsMessage &message, int peer)
{
if (peer < 0)
return false;
auto session = Blam::Network::GetActiveSession();
// if we are trying to send something to ourself, pretend someone else sent it to us.
if (peer == session->MembershipInfo.LocalPeerIndex)
{
ReceivedHaloStatsMessage(session, peer, message);
return true;
}
auto packet = HaloStatsPacketSender->New();
packet.Data = message;
HaloStatsPacketSender->Send(peer, packet);
return true;
}
void AddMessageHandler(std::shared_ptr<HaloStatsMessageHandler> handler)
{
haloStatsMessageHandlers.push_back(handler);
}
class HaloStatsOutputHandler : public HaloStatsMessageHandler
{
public:
HaloStatsOutputHandler(){}
void MessageReceived(const HaloStatsMessage &message) override
{
if (message.Type == HaloStatsMessageType::Rank)
{
rapidjson::StringBuffer jsonBuffer;
rapidjson::Writer<rapidjson::StringBuffer> jsonWriter(jsonBuffer);
jsonWriter.StartObject();
jsonWriter.Key("Rank");
jsonWriter.Int(message.rank);
jsonWriter.EndObject();
Web::Ui::ScreenLayer::Notify("HaloStatsRankMessage", jsonBuffer.GetString(), true);
}
}
};
void Init()
{
auto handler = std::make_shared<HaloStatsMessagePacketHandler>();
HaloStatsPacketSender = Patches::CustomPackets::RegisterPacket<HaloStatsMessage>("eldewrito-halostats-message", handler);
}
}
}
namespace Web
{
namespace Ui
{
namespace HaloStats
{
void Init()
{
Server::HaloStatsPackets::AddMessageHandler(std::make_shared<Server::HaloStatsPackets::HaloStatsOutputHandler>());
}
void Show()
{
ScreenLayer::Show("halostats", "{}");
}
void Hide()
{
ScreenLayer::Hide("halostats");
}
}
}
}
#pragma once
#include <string>
#include <d3dx9core.h>
#include <chrono>
#include "../Blam/BlamEvents.hpp"
#include "../Patches/Events.hpp"
#include <vector>
#include <memory>
#include "../Utils/Utils.hpp"
namespace Web
{
namespace Ui
{
namespace HaloStats
{
void Init();
void Show();
void Hide();
}
}
}
namespace Server
{
namespace HaloStatsPackets
{
void Init();
enum class HaloStatsMessageType : uint32_t
{
// A request packet for the client to send the values
Rank,
Achievement,
Banned,
// valid message types.
Count
};
// Voting Message Data
struct HaloStatsMessage
{
HaloStatsMessage() { }
HaloStatsMessage(HaloStatsMessageType type);
// The message type.
HaloStatsMessageType Type;
int rank;
int achievementID;
};
class HaloStatsMessageHandler
{
public:
virtual ~HaloStatsMessageHandler() { }
// Called after a message has been received.
virtual void MessageReceived(const HaloStatsMessage &message) = 0;
};
void AddMessageHandler(std::shared_ptr<HaloStatsMessageHandler> handler);
}
}
\ No newline at end of file
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
font-family: "Tahoma", sans-serif;
}
*, *:before, *:after {
box-sizing: border-box;
}
html, body {
font-family: 'Roboto', sans-serif;
font-size: 1em;
}
.button:hover
{
background-color: #5dbe83;
color: #ffffff;
}
.button {
display: block;
width: 200px;
text-align: center;
margin: 10px auto 0;
border: 1px solid #5dbe83;
color: #5dbe83;
padding: 10px;
border-radius: 3px;
cursor: pointer;
transition: all .25s ease;
position: relative;
z-index: 3;
background-color: white;
}
.overlay {
position: fixed;
height: 100%;
width: 100%;
background-color: rgba(0,0,0,0.05);
top:0;
left:0;
z-index: 3;
}
.notification.show{
visibility: visible;
}
.notification > div
{
display: table-cell;
}
.notification .impact
{
z-index:2;
position: relative;
width: 50px;
background-color: #404040;
vertical-align: top;
display: table-cell;
}
.notification .impact .icon{
margin-left: 3px;
margin-top: 3px;
vertical-align:bottom;
height: 57px;
display: table;
}
.notification .impact .fa{
color: #ffffff;;
text-align: center;
vertical-align: middle;
display: table-cell;
font-size: 46px;
}
.notification .message-container{
width: 0;
overflow: hidden;
display: block;
transition: width .5s ease;
position: relative;
}
.notification .message-container .close{
color: #404040;
font-size: 1.5em;
position: absolute;
display: block;
top: 0;
right: 5px;
cursor: pointer;
}
.notification .message
{
width: 260px;
background-color: #ABABAB;
padding: 4px 10px 4px;
position: relative;
}
.notification .message p
{
font-size: 12px;
line-height: 145%;
margin-bottom: 0;
color: #404040;
}
.notification .message strong
{
display: block;
margin-bottom: 9px;
}
.notification {
z-index: 4;
opacity: 0;
visibility: hidden;
transition: opacity .5s ease, transform .75s ease;
margin: 20px auto 50px;
min-height: 32px;
box-shadow: 0 0 2px rgba(0,0,0, .3);
position: relative;
display: table;
}
}
.notification.positive {
transition: transform .5 ease;
transform: translate3d(0,0,0) rotateY(45deg);
}
.notification.positive .fa{
}
.notification.positive strong{
display: table-cell;
vertical-align: middle;
margin-bottom: 0;
color: black;
height: 60px;
}
.notification.open--rot
{
opacity: 1;
transform: translate3d(0,0,0) rotateY(0deg);
}
.notification.open--rot.open--width .message-container
{
width: 260px;
}
.notification-hidden {
display: none;
}
\ No newline at end of file
var messageQueue = [];
var messageInProgress = false;
$("html").on("keydown", function(e) {
if (e.which == 113)
dew.hide();
});
$(window).load(function() {
$(function() {
$('.notification').addClass('notification-hidden');
})
});
function openNotification(whichNotification) {
var obj = messageQueue[0];
document.getElementById('messageText').innerHTML = obj.text;
$('#messageImage').attr("src", 'images/' + obj.image + '.png');
messageInProgress = true;
var thisNotification = $('.notification.' + whichNotification);
thisNotification.removeClass('notification-hidden');
setTimeout(function() {
play();
thisNotification.addClass('open');
$('.notification.open').addClass('show').addClass('open--rot');
setTimeout(function() {
$('.notification.open').addClass('open--width');
}, 750);
$('body').append('<div class="overlay"></div>');
}, 50);
}
function closeNotification() {
var type = $('.notification.open');
type.removeClass('open--width');
setTimeout(function() {
type.removeClass('open--rot')
}, 400);
setTimeout(function() {
type.removeClass('show');
}, 600);
setTimeout(function() {
$('.overlay').remove();
type.removeClass('open');
}, 800);
setTimeout(function() {
type.addClass('notification-hidden')
messageInProgress = false;
messageQueue.shift();
checkQueue();
}, 850);
}
function play() {
var audioElement = new Audio('sounds/AchievementUnlocked.ogg');
audioElement.play();
}
dew.on("HaloStatsRankMessage", function(event) {
dew.show();
var image = {
"text": "You have been Promoted! " + event.data.RankName,
"image": event.data.Rank
};
messageQueue.push(image);
if (messageInProgress == false || messageQueue.length == 1) {
openNotification('positive');
setTimeout(function() {
closeNotification();
}, 4000);
}
});
function checkQueue() {
if (messageQueue.length > 0) {
openNotification('positive');
setTimeout(function() {
closeNotification();
}, 4000);
}
}
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Halostats</title>
<link rel="stylesheet" type="text/css" href="halostats.css" />
<script src="dew://lib/dew.js"></script>
<script src="dew://lib/jquery-2.2.1.min.js"></script>
<script src="halostats.js"></script>
</head>
<body>
<div class="container">
<div class="notification positive">
<div class="impact">
<img id="messageImage" class="icon" > </img>
</div>
<div class="message-container">
<div class="message">
<strong id="messageText"> </strong>
</div>
</div>