Browse Source

Added json/fusion shit :D also moving people around

master
Joshua Moerman 11 years ago
parent
commit
2b2cc87239
  1. 123
      json.h
  2. 128
      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;
}

128
main.cpp

@ -2,57 +2,92 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <chrono>
#include <cmath>
#include <lib/libwebsockets.h> #include <lib/libwebsockets.h>
#include <json_spirit/json_spirit.h> #include "json.h"
#include "websockets.h" #include "websockets.h"
namespace js = json_spirit; template <typename C>
inline js::Value ptrvector_to_json(C const & container){
js::Object get_object(std::string const & in){ js::Array array;
js::Value value; for(auto x : container){
js::read(in, value); array.push_back(to_json(*x));
return value.getObject(); }
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; int uid = 0;
struct User { struct User {
unsigned int index{0}; unsigned int index{0};
std::string name{"Guest"}; std::string name{"Unknown guest"};
int width{0}; Size size{0,0};
int height{0}; Position position{0,0};
static User from_json(const js::Object& obj){ User()
User ret; : index(uid++)
#define in(x, f) ret.x = obj.at(#x).f() {}
in(name, getString);
in(width, getInt);
in(height, getInt);
#undef in
return ret;
}
js::Object to_json() const { User& operator=(IncomingPacket const & p){
js::Object obj; name = p.name;
#define out(x) obj[#x] = x size = p.size;
out(name); return *this;
out(width);
out(height);
#undef out
return obj;
} }
}; };
BOOST_FUSION_ADAPT_STRUCT(
User,
(unsigned int, index)
(std::string, name)
(Size, size)
(Position, position)
)
// unfortunately libwebsockets owns the user thingy // unfortunately libwebsockets owns the user thingy
std::vector<User*> people_online; 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{ websockets::TestProtocol<User> default_protocol{
// connection established // connection established
[](User& user){ [](User& user){
user.name = "Unknown guest";
user.index = uid++;
people_online.push_back(&user); people_online.push_back(&user);
}, },
// connection closed // connection closed
@ -61,23 +96,20 @@ websockets::TestProtocol<User> default_protocol{
}, },
// write (will always come after receive) // write (will always come after receive)
[](User& user) -> std::string{ [](User& user) -> std::string{
std::string string_to_send = "Other People:"; update_positions();
for(auto x : people_online) if(x != &user) string_to_send += " " + x->name; return write_json(ptrvector_to_json(people_online));
return string_to_send;
}, },
// receive // receive
[](User& user, std::string in){ [](User& user, std::string in){
try{ try{
user = User::from_json(get_object(in)); user = from_json<IncomingPacket>(parse_json(in));
} catch(std::exception& e) { } catch(std::exception& e) {
throw websockets::runtime_error(e.what()); throw websockets::runtime_error(e.what());
} }
} }
}; };
struct Empty{ struct Empty{};
int x;
};
websockets::TestProtocol<Empty> observer_protocol{ websockets::TestProtocol<Empty> observer_protocol{
// connection established // connection established
@ -86,23 +118,19 @@ websockets::TestProtocol<Empty> observer_protocol{
[](Empty& user){}, [](Empty& user){},
// write (will always come after receive) // write (will always come after receive)
[](Empty& user) -> std::string{ [](Empty& user) -> std::string{
js::Array array; update_positions();
for(auto x : people_online){ return write_json(ptrvector_to_json(people_online));
array.push_back(x->to_json());
}
js::Value value(array);
return write(value);
}, },
// receive // receive
[](Empty& user, std::string in){} [](Empty& user, std::string in){}
}; };
static 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){ 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
};
return websockets::default_main(argc, argv, protocols); 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); websockets::Log log("lwsts", LOG_PID | LOG_PERROR, 7);
log.notice("Running in server mode\n");
lws_context_creation_info info; lws_context_creation_info info;
memset(&info, 0, sizeof info); memset(&info, 0, sizeof info);

7
websockets.h

@ -1,4 +1,4 @@
#include <iostream>
#include <string> #include <string>
#include <memory> #include <memory>
#include <functional> #include <functional>
@ -100,6 +100,8 @@ namespace websockets {
std::function<std::string(user_type&)> write_func; std::function<std::string(user_type&)> write_func;
std::function<void(user_type&, std::string)> receive_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) 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) : establish_func(establish_func)
, close_func(close_func) , close_func(close_func)
@ -112,15 +114,18 @@ namespace websockets {
try{ try{
switch (reason) { switch (reason) {
case LWS_CALLBACK_ESTABLISHED: case LWS_CALLBACK_ESTABLISHED:
lwsl_notice("Connection established (%p, %p)\n", this, user_ptr);
new (user_ptr) user_type(); new (user_ptr) user_type();
establish_func(user); establish_func(user);
break; break;
case LWS_CALLBACK_CLOSED: case LWS_CALLBACK_CLOSED:
lwsl_notice("Connection closed (%p, %p)\n", this, user_ptr);
user.~user_type(); user.~user_type();
close_func(user); close_func(user);
break; break;
case LWS_CALLBACK_SERVER_WRITEABLE:{ case LWS_CALLBACK_SERVER_WRITEABLE:{
std::string string_to_send = write_func(user); 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 :( // 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]; 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()); memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size());