Browse Source

Added json/fusion shit :D also moving people around

master
Joshua Moerman 12 years ago
parent
commit
2b2cc87239
  1. 123
      json.h
  2. 120
      main.cpp
  3. 1
      websockets.cpp
  4. 7
      websockets.h

123
json.h

@ -0,0 +1,123 @@
#pragma once
#include <utility>
#include <type_traits>
#include <stdexcept>
#include <boost/fusion/include/define_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at.hpp>
#include <json_spirit/json_spirit.h>
namespace js = json_spirit;
inline js::Value parse_json(std::string const & in){
js::Value value;
js::read(in, value);
return value;
}
inline std::string write_json(js::Value const & v){
return js::write(v);
}
/*
*** WRITING ***
Note: Does not yet support arrays, only basic types, std::strings and user defined types (with boost::fusion adaption)
*/
// Basic types
template <typename T>
typename std::enable_if<!std::is_class<T>::value && !std::is_pointer<T>::value, js::Value>::type to_json(T const & x){
return x;
}
inline js::Value to_json(std::string const & x){
return x;
}
// Compound types (need recursion)
template <typename T>
typename std::enable_if<std::is_class<T>::value, js::Value>::type to_json(T const & x);
// boost::fusion magics
template<typename T, typename N>
struct write_itr{
typedef typename boost::mpl::next<N>::type next_t;
typedef boost::fusion::extension::struct_member_name<T, N::value> name_t;
static inline void exec(js::Object & obj, const T& x){
obj[name_t::call()] = to_json(boost::fusion::at<N>(x));
write_itr<T, next_t>::exec(obj, x);
}
};
template<typename T>
struct write_itr<T, typename boost::fusion::result_of::size<T>::type>{
static inline void exec(js::Object & obj, const T& x) {}
};
// Compound types (need recursion)
template <typename T>
typename std::enable_if<std::is_class<T>::value, js::Value>::type to_json(T const & x){
js::Object obj;
write_itr<T, boost::mpl::int_<0>>::exec(obj, x);
return obj;
}
/*
*** READING ***
Note: Does not yet support arrays, only basic types, std::strings and user defined types (with boost::fusion adaption)
*/
// Basic types
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type from_json(js::Value const & x){
return x.getInt64();
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type from_json(js::Value const & x){
return x.getReal();
}
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, T>::type from_json(js::Value const & x){
return x.getString();
}
// Compound types (need recursion)
template <typename T>
typename std::enable_if<std::is_class<T>::value && !std::is_same<T, std::string>::value, T>::type from_json(js::Value const & x);
// boost::fusion magics
template<typename T, typename N>
struct read_itr{
typedef typename boost::fusion::result_of::value_at<T, N>::type current_t;
typedef typename boost::mpl::next<N>::type next_t;
typedef boost::fusion::extension::struct_member_name<T, N::value> name_t;
static inline void exec(js::Object const & obj, T& x){
try{
boost::fusion::at<N>(x) = from_json<current_t>(obj.at(name_t::call()));
} catch(std::out_of_range& e) {
throw std::out_of_range(e.what() + std::string(": ") + name_t::call());
}
read_itr<T, next_t>::exec(obj, x);
}
};
template<typename T>
struct read_itr<T, typename boost::fusion::result_of::size<T>::type>{
static inline void exec(js::Object const & obj, T& x) {}
};
// Compound types (need recursion)
template <typename T>
typename std::enable_if<std::is_class<T>::value && !std::is_same<T, std::string>::value, T>::type from_json(js::Value const & x){
T ret;
read_itr<T, boost::mpl::int_<0>>::exec(x.getObject(), ret);
return ret;
}

120
main.cpp

@ -2,57 +2,92 @@
#include <string>
#include <vector>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <lib/libwebsockets.h>
#include <json_spirit/json_spirit.h>
#include "json.h"
#include "websockets.h"
namespace js = json_spirit;
js::Object get_object(std::string const & in){
js::Value value;
js::read(in, value);
return value.getObject();
template <typename C>
inline js::Value ptrvector_to_json(C const & container){
js::Array array;
for(auto x : container){
array.push_back(to_json(*x));
}
return array;
}
BOOST_FUSION_DEFINE_STRUCT(
, Size,
(int, width)
(int, height)
)
BOOST_FUSION_DEFINE_STRUCT(
, Position,
(int, x)
(int, y)
)
BOOST_FUSION_DEFINE_STRUCT(
, IncomingPacket,
(std::string, name)
(Size, size)
)
int uid = 0;
struct User {
unsigned int index{0};
std::string name{"Guest"};
int width{0};
int height{0};
static User from_json(const js::Object& obj){
User ret;
#define in(x, f) ret.x = obj.at(#x).f()
in(name, getString);
in(width, getInt);
in(height, getInt);
#undef in
return ret;
}
std::string name{"Unknown guest"};
Size size{0,0};
Position position{0,0};
js::Object to_json() const {
js::Object obj;
#define out(x) obj[#x] = x
out(name);
out(width);
out(height);
#undef out
return obj;
User()
: index(uid++)
{}
User& operator=(IncomingPacket const & p){
name = p.name;
size = p.size;
return *this;
}
};
BOOST_FUSION_ADAPT_STRUCT(
User,
(unsigned int, index)
(std::string, name)
(Size, size)
(Position, position)
)
// unfortunately libwebsockets owns the user thingy
std::vector<User*> people_online;
inline void update_positions(){
typedef std::chrono::duration<double, std::ratio<1,1>> fseconds;
static auto start = std::chrono::steady_clock::now();
auto end = std::chrono::steady_clock::now();
double time = std::chrono::duration_cast<fseconds>(end-start).count();
//std::cout << time << std::endl;
auto n = people_online.size();
for(int i = 0; i < n; ++i){
User& user = *people_online[i];
double x = cos(0.1 * time + 2*M_PI*i/double(n));
double y = sin(0.1 * time + 2*M_PI*i/double(n));
user.position.x = 200 + 180*x;
user.position.y = 200 + 180*y;
}
}
websockets::TestProtocol<User> default_protocol{
// connection established
[](User& user){
user.name = "Unknown guest";
user.index = uid++;
people_online.push_back(&user);
},
// connection closed
@ -61,23 +96,20 @@ websockets::TestProtocol<User> default_protocol{
},
// write (will always come after receive)
[](User& user) -> std::string{
std::string string_to_send = "Other People:";
for(auto x : people_online) if(x != &user) string_to_send += " " + x->name;
return string_to_send;
update_positions();
return write_json(ptrvector_to_json(people_online));
},
// receive
[](User& user, std::string in){
try{
user = User::from_json(get_object(in));
user = from_json<IncomingPacket>(parse_json(in));
} catch(std::exception& e) {
throw websockets::runtime_error(e.what());
}
}
};
struct Empty{
int x;
};
struct Empty{};
websockets::TestProtocol<Empty> observer_protocol{
// connection established
@ -86,23 +118,19 @@ websockets::TestProtocol<Empty> observer_protocol{
[](Empty& user){},
// write (will always come after receive)
[](Empty& user) -> std::string{
js::Array array;
for(auto x : people_online){
array.push_back(x->to_json());
}
js::Value value(array);
return write(value);
update_positions();
return write_json(ptrvector_to_json(people_online));
},
// receive
[](Empty& user, std::string in){}
};
static libwebsocket_protocols protocols[] = {
int main(int argc, char **argv){
libwebsocket_protocols protocols[] = {
WSstandard_protocol("default", default_protocol),
WSstandard_protocol("observer", observer_protocol),
{ NULL, NULL, 0 } // end of list
};
int main(int argc, char **argv){
return websockets::default_main(argc, argv, protocols);
}

1
websockets.cpp

@ -42,7 +42,6 @@ namespace websockets {
}
websockets::Log log("lwsts", LOG_PID | LOG_PERROR, 7);
log.notice("Running in server mode\n");
lws_context_creation_info info;
memset(&info, 0, sizeof info);

7
websockets.h

@ -1,4 +1,4 @@
#include <iostream>
#include <string>
#include <memory>
#include <functional>
@ -100,6 +100,8 @@ namespace websockets {
std::function<std::string(user_type&)> write_func;
std::function<void(user_type&, std::string)> receive_func;
bool verbose{false};
TestProtocol(decltype(establish_func) establish_func, decltype(close_func) close_func, decltype(write_func) write_func, decltype(receive_func) receive_func)
: establish_func(establish_func)
, close_func(close_func)
@ -112,15 +114,18 @@ namespace websockets {
try{
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
lwsl_notice("Connection established (%p, %p)\n", this, user_ptr);
new (user_ptr) user_type();
establish_func(user);
break;
case LWS_CALLBACK_CLOSED:
lwsl_notice("Connection closed (%p, %p)\n", this, user_ptr);
user.~user_type();
close_func(user);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:{
std::string string_to_send = write_func(user);
if(verbose) std::cout << string_to_send << std::endl;
// we need the extra bytes padding on both sides :(
unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING];
memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size());