Merge remote-tracking branch 'upstream/master'

This commit is contained in:
LionTurtle 2013-09-11 16:45:29 -07:00
commit bb1feab661
25 changed files with 33682 additions and 284 deletions

View file

@ -26,7 +26,6 @@ const char CHILD_TARGET_NAME[] = "assignment-client";
pid_t* childForks = NULL;
sockaddr_in customAssignmentSocket = {};
const char* assignmentPool = NULL;
int numForks = 0;
void childClient() {
@ -51,8 +50,10 @@ void childClient() {
unsigned char packetData[MAX_PACKET_SIZE];
ssize_t receivedBytes = 0;
sockaddr_in senderSocket = {};
// create a request assignment, accept all assignments, pass the desired pool (if it exists)
Assignment requestAssignment(Assignment::Request, Assignment::All, assignmentPool);
Assignment requestAssignment(Assignment::RequestCommand, Assignment::AllTypes);
while (true) {
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
@ -61,22 +62,34 @@ void childClient() {
nodeList->sendAssignment(requestAssignment);
}
if (nodeList->getNodeSocket()->receive(packetData, &receivedBytes) &&
packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT && packetVersionMatch(packetData)) {
if (nodeList->getNodeSocket()->receive((sockaddr*) &senderSocket, packetData, &receivedBytes) &&
(packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT)
&& packetVersionMatch(packetData)) {
// construct the deployed assignment from the packet data
Assignment deployedAssignment(packetData, receivedBytes);
qDebug() << "Received an assignment -" << deployedAssignment << "\n";
// switch our nodelist DOMAIN_IP to the ip receieved in the assignment
if (deployedAssignment.getAttachedPublicSocket()->sa_family == AF_INET) {
in_addr domainSocketAddr = ((sockaddr_in*) deployedAssignment.getAttachedPublicSocket())->sin_addr;
// switch our nodelist DOMAIN_IP
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT ||
deployedAssignment.getAttachedPublicSocket()->sa_family == AF_INET) {
in_addr domainSocketAddr = {};
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
// the domain server IP address is the address we got this packet from
domainSocketAddr = senderSocket.sin_addr;
} else {
// grab the domain server IP address from the packet from the AS
domainSocketAddr = ((sockaddr_in*) deployedAssignment.getAttachedPublicSocket())->sin_addr;
}
nodeList->setDomainIP(inet_ntoa(domainSocketAddr));
qDebug("Destination IP for assignment is %s\n", inet_ntoa(domainSocketAddr));
if (deployedAssignment.getType() == Assignment::AudioMixer) {
if (deployedAssignment.getType() == Assignment::AudioMixerType) {
AudioMixer::run();
} else {
AvatarMixer::run();
@ -165,19 +178,23 @@ int main(int argc, const char* argv[]) {
// start the Logging class with the parent's target name
Logging::setTargetName(PARENT_TARGET_NAME);
const char CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION[] = "-a";
const char CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION[] = "-p";
// grab the overriden assignment-server hostname from argv, if it exists
const char* customAssignmentServer = getCmdOption(argc, argv, "-a");
if (customAssignmentServer) {
::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServer, ASSIGNMENT_SERVER_PORT);
const char* customAssignmentServerHostname = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION);
if (customAssignmentServerHostname) {
const char* customAssignmentServerPortString = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION);
unsigned short assignmentServerPort = customAssignmentServerPortString
? atoi(customAssignmentServerPortString) : ASSIGNMENT_SERVER_PORT;
::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServerHostname, assignmentServerPort);
}
const char* NUM_FORKS_PARAMETER = "-n";
const char* numForksString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER);
// grab the assignment pool from argv, if it was passed
const char* ASSIGNMENT_POOL_PARAMETER = "-p";
::assignmentPool = getCmdOption(argc, argv, ASSIGNMENT_POOL_PARAMETER);
int processID = 0;
if (numForksString) {

View file

@ -8,11 +8,12 @@
#include <arpa/inet.h>
#include <fstream>
#include <queue>
#include <deque>
#include <QtCore/QString>
#include <Assignment.h>
#include <Logging.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UDPSocket.h>
@ -22,6 +23,8 @@ const long long NUM_DEFAULT_ASSIGNMENT_STALENESS_USECS = 10 * 1000 * 1000;
int main(int argc, const char* argv[]) {
qInstallMessageHandler(Logging::verboseMessageHandler);
std::deque<Assignment*> assignmentQueue;
sockaddr_in senderSocket;
@ -39,8 +42,8 @@ int main(int argc, const char* argv[]) {
// construct the requested assignment from the packet data
Assignment requestAssignment(senderData, receivedBytes);
qDebug() << "Received request for assignment:" << requestAssignment;
qDebug() << "Current queue size is" << assignmentQueue.size();
qDebug() << "Received request for assignment:" << requestAssignment << "\n";
qDebug() << "Current queue size is" << assignmentQueue.size() << "\n";
// make sure there are assignments in the queue at all
if (assignmentQueue.size() > 0) {
@ -59,51 +62,38 @@ int main(int argc, const char* argv[]) {
continue;
}
bool eitherHasPool = ((*assignment)->getPool() || requestAssignment.getPool());
bool bothHavePool = ((*assignment)->getPool() && requestAssignment.getPool());
// make sure there is a pool match for the created and requested assignment
// or that neither has a designated pool
if ((eitherHasPool && bothHavePool
&& strcmp((*assignment)->getPool(), requestAssignment.getPool()) == 0)
|| !eitherHasPool) {
// check if the requestor is on the same network as the destination for the assignment
if (senderSocket.sin_addr.s_addr ==
((sockaddr_in*) (*assignment)->getAttachedPublicSocket())->sin_addr.s_addr) {
// if this is the case we remove the public socket on the assignment by setting it to NULL
// this ensures the local IP and port sent to the requestor is the local address of destination
(*assignment)->setAttachedPublicSocket(NULL);
}
int numAssignmentBytes = (*assignment)->packToBuffer(assignmentPacket + numSendHeaderBytes);
// send the assignment
serverSocket.send((sockaddr*) &senderSocket,
assignmentPacket,
numSendHeaderBytes + numAssignmentBytes);
// delete this assignment now that it has been sent out
delete *assignment;
// remove it from the deque and make the iterator the next assignment
assignmentQueue.erase(assignment);
// stop looping - we've handed out an assignment
break;
} else {
// push forward the iterator
assignment++;
// check if the requestor is on the same network as the destination for the assignment
if (senderSocket.sin_addr.s_addr ==
((sockaddr_in*) (*assignment)->getAttachedPublicSocket())->sin_addr.s_addr) {
// if this is the case we remove the public socket on the assignment by setting it to NULL
// this ensures the local IP and port sent to the requestor is the local address of destination
(*assignment)->setAttachedPublicSocket(NULL);
}
int numAssignmentBytes = (*assignment)->packToBuffer(assignmentPacket + numSendHeaderBytes);
// send the assignment
serverSocket.send((sockaddr*) &senderSocket,
assignmentPacket,
numSendHeaderBytes + numAssignmentBytes);
// delete this assignment now that it has been sent out
delete *assignment;
// remove it from the deque and make the iterator the next assignment
assignmentQueue.erase(assignment);
// stop looping - we've handed out an assignment
break;
}
}
} else if (senderData[0] == PACKET_TYPE_CREATE_ASSIGNMENT && packetVersionMatch(senderData)) {
// construct the create assignment from the packet data
Assignment* createdAssignment = new Assignment(senderData, receivedBytes);
qDebug() << "Received a created assignment:" << *createdAssignment;
qDebug() << "Current queue size is" << assignmentQueue.size();
qDebug() << "Received a created assignment:" << *createdAssignment << "\n";
qDebug() << "Current queue size is" << assignmentQueue.size() << "\n";
// assignment server is likely on a public server
// assume that the address we now have for the sender is the public address/port

View file

@ -8,6 +8,15 @@ set(TARGET_NAME domain-server)
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)
# remove and then copy the files for the webserver
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory
$<TARGET_FILE_DIR:${TARGET_NAME}>/web)
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/src/web"
$<TARGET_FILE_DIR:${TARGET_NAME}>/web)
# link the shared hifi library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})

View file

@ -19,6 +19,7 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <deque>
#include <map>
#include <math.h>
#include <string.h>
@ -32,6 +33,8 @@
#include "PacketHeaders.h"
#include "SharedUtil.h"
#include "mongoose.h"
const int DOMAIN_LISTEN_PORT = 40102;
unsigned char packetData[MAX_PACKET_SIZE];
@ -49,7 +52,11 @@ unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* no
}
int main(int argc, const char* argv[]) {
qInstallMessageHandler(Logging::verboseMessageHandler);
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, DOMAIN_LISTEN_PORT);
// If user asks to run in "local" mode then we do NOT replace the IP
// with the EC2 IP. Otherwise, we will replace the IP like we used to
// this allows developers to run a local domain without recompiling the
@ -71,7 +78,6 @@ int main(int argc, const char* argv[]) {
char nodeType = '\0';
unsigned char broadcastPacket[MAX_PACKET_SIZE];
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
unsigned char* currentBufferPos;
unsigned char* startPointer;
@ -84,13 +90,8 @@ int main(int argc, const char* argv[]) {
nodeList->startSilentNodeRemovalThread();
timeval lastStatSendTime = {};
const char ASSIGNMENT_POOL_OPTION[] = "-p";
const char ASSIGNMENT_SERVER_OPTION[] = "-a";
// set our assignment pool from argv, if it exists
const char* assignmentPool = getCmdOption(argc, argv, ASSIGNMENT_POOL_OPTION);
// grab the overriden assignment-server hostname from argv, if it exists
const char* customAssignmentServer = getCmdOption(argc, argv, ASSIGNMENT_SERVER_OPTION);
if (customAssignmentServer) {
@ -99,149 +100,209 @@ int main(int argc, const char* argv[]) {
}
// use a map to keep track of iterations of silence for assignment creation requests
const long long ASSIGNMENT_SILENCE_MAX_USECS = 5 * 1000 * 1000;
const long long GLOBAL_ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000;
timeval lastGlobalAssignmentRequest = {};
// setup the assignment queue
std::deque<Assignment*> assignmentQueue;
// as a domain-server we will always want an audio mixer and avatar mixer
// setup the create assignment pointers for those
Assignment* audioAssignment = NULL;
Assignment* avatarAssignment = NULL;
// setup the create assignments for those
Assignment audioMixerAssignment(Assignment::CreateCommand,
Assignment::AudioMixerType,
Assignment::LocalLocation);
// construct a local socket to send with our created assignments
Assignment avatarMixerAssignment(Assignment::CreateCommand,
Assignment::AvatarMixerType,
Assignment::LocalLocation);
// construct a local socket to send with our created assignments to the global AS
sockaddr_in localSocket = {};
localSocket.sin_family = AF_INET;
localSocket.sin_port = htons(nodeList->getInstance()->getNodeSocket()->getListeningPort());
localSocket.sin_addr.s_addr = serverLocalAddress;
// setup the mongoose web server
struct mg_context *ctx;
struct mg_callbacks callbacks = {};
// list of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080",
"document_root", "./web", NULL};
// Start the web server.
ctx = mg_start(&callbacks, NULL, options);
while (true) {
if (!nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER)) {
if (!audioAssignment
|| usecTimestampNow() - usecTimestamp(&audioAssignment->getTime()) >= ASSIGNMENT_SILENCE_MAX_USECS) {
if (!audioAssignment) {
audioAssignment = new Assignment(Assignment::Create, Assignment::AudioMixer, assignmentPool);
audioAssignment->setAttachedLocalSocket((sockaddr*) &localSocket);
}
nodeList->sendAssignment(*audioAssignment);
audioAssignment->setCreateTimeToNow();
}
// check if our audio-mixer or avatar-mixer are dead and we don't have existing assignments in the queue
// so we can add those assignments back to the front of the queue since they are high-priority
if (!nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER) &&
std::find(assignmentQueue.begin(), assignmentQueue.end(), &avatarMixerAssignment) == assignmentQueue.end()) {
qDebug("Missing an avatar mixer and assignment not in queue. Adding.\n");
assignmentQueue.push_front(&avatarMixerAssignment);
}
if (!nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER)) {
if (!avatarAssignment
|| usecTimestampNow() - usecTimestamp(&avatarAssignment->getTime()) >= ASSIGNMENT_SILENCE_MAX_USECS) {
if (!avatarAssignment) {
avatarAssignment = new Assignment(Assignment::Create, Assignment::AvatarMixer, assignmentPool);
avatarAssignment->setAttachedLocalSocket((sockaddr*) &localSocket);
}
nodeList->sendAssignment(*avatarAssignment);
// reset the create time on the assignment so re-request is in ASSIGNMENT_SILENCE_MAX_USECS
avatarAssignment->setCreateTimeToNow();
}
if (!nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER) &&
std::find(assignmentQueue.begin(), assignmentQueue.end(), &audioMixerAssignment) == assignmentQueue.end()) {
qDebug("Missing an audio mixer and assignment not in queue. Adding.\n");
assignmentQueue.push_front(&audioMixerAssignment);
}
if (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) &&
(packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) &&
packetVersionMatch(packetData)) {
// this is an RFD or domain list request packet, and there is a version match
std::map<char, Node *> newestSoloNodes;
int numBytesSenderHeader = numBytesForPacketHeader(packetData);
nodeType = *(packetData + numBytesSenderHeader);
int numBytesSocket = unpackSocket(packetData + numBytesSenderHeader + sizeof(NODE_TYPE),
(sockaddr*) &nodeLocalAddress);
sockaddr* destinationSocket = (sockaddr*) &nodePublicAddress;
// check the node public address
// if it matches our local address we're on the same box
// so hardcode the EC2 public address for now
if (nodePublicAddress.sin_addr.s_addr == serverLocalAddress) {
// If we're not running "local" then we do replace the IP
// with 0. This designates to clients that the server is reachable
// at the same IP address
if (!isLocalMode) {
nodePublicAddress.sin_addr.s_addr = 0;
destinationSocket = (sockaddr*) &nodeLocalAddress;
}
}
Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress,
(sockaddr*) &nodeLocalAddress,
nodeType,
nodeList->getLastNodeID());
// if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it
if (newNode) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
while (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) &&
packetVersionMatch(packetData)) {
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) {
// this is an RFD or domain list request packet, and there is a version match
std::map<char, Node *> newestSoloNodes;
int numBytesSenderHeader = numBytesForPacketHeader(packetData);
nodeType = *(packetData + numBytesSenderHeader);
int numBytesSocket = unpackSocket(packetData + numBytesSenderHeader + sizeof(NODE_TYPE),
(sockaddr*) &nodeLocalAddress);
sockaddr* destinationSocket = (sockaddr*) &nodePublicAddress;
// check the node public address
// if it matches our local address we're on the same box
// so hardcode the EC2 public address for now
if (nodePublicAddress.sin_addr.s_addr == serverLocalAddress) {
// If we're not running "local" then we do replace the IP
// with 0. This designates to clients that the server is reachable
// at the same IP address
if (!isLocalMode) {
nodePublicAddress.sin_addr.s_addr = 0;
destinationSocket = (sockaddr*) &nodeLocalAddress;
}
}
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress,
(sockaddr*) &nodeLocalAddress,
nodeType,
nodeList->getLastNodeID());
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE)
+ numBytesSocket + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// this is not the node themselves
// and this is an node of a type in the passed node types of interest
// or the node did not pass us any specific types they are interested in
if (memchr(SOLO_NODE_TYPES, node->getType(), sizeof(SOLO_NODE_TYPES)) == NULL) {
// this is an node of which there can be multiple, just add them to the packet
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
// if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it
if (newNode) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
}
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE)
+ numBytesSocket + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// this is not the node themselves
// and this is an node of a type in the passed node types of interest
// or the node did not pass us any specific types they are interested in
} else {
// solo node, we need to only send newest
if (newestSoloNodes[node->getType()] == NULL ||
newestSoloNodes[node->getType()]->getWakeMicrostamp() < node->getWakeMicrostamp()) {
// we have to set the newer solo node to add it to the broadcast later
newestSoloNodes[node->getType()] = &(*node);
if (memchr(SOLO_NODE_TYPES, node->getType(), sizeof(SOLO_NODE_TYPES)) == NULL) {
// this is an node of which there can be multiple, just add them to the packet
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
} else {
// solo node, we need to only send newest
if (newestSoloNodes[node->getType()] == NULL ||
newestSoloNodes[node->getType()]->getWakeMicrostamp() < node->getWakeMicrostamp()) {
// we have to set the newer solo node to add it to the broadcast later
newestSoloNodes[node->getType()] = &(*node);
}
}
}
}
for (std::map<char, Node *>::iterator soloNode = newestSoloNodes.begin();
soloNode != newestSoloNodes.end();
soloNode++) {
// this is the newest alive solo node, add them to the packet
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, soloNode->second);
}
}
for (std::map<char, Node *>::iterator soloNode = newestSoloNodes.begin();
soloNode != newestSoloNodes.end();
soloNode++) {
// this is the newest alive solo node, add them to the packet
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, soloNode->second);
// update last receive to now
uint64_t timeNow = usecTimestampNow();
newNode->setLastHeardMicrostamp(timeNow);
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
&& memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
newNode->setWakeMicrostamp(timeNow);
}
// add the node ID to the end of the pointer
currentBufferPos += packNodeId(currentBufferPos, newNode->getNodeID());
// send the constructed list back to this node
nodeList->getNodeSocket()->send(destinationSocket,
broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes);
}
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
// update last receive to now
uint64_t timeNow = usecTimestampNow();
newNode->setLastHeardMicrostamp(timeNow);
qDebug("Received a request for assignment.\n");
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
&& memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
newNode->setWakeMicrostamp(timeNow);
// this is an unassigned client talking to us directly for an assignment
// go through our queue and see if there are any assignments to give out
std::deque<Assignment*>::iterator assignment = assignmentQueue.begin();
while (assignment != assignmentQueue.end()) {
// give this assignment out, no conditions stop us from giving it to the local assignment client
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
int numAssignmentBytes = (*assignment)->packToBuffer(broadcastPacket + numHeaderBytes);
nodeList->getNodeSocket()->send((sockaddr*) &nodePublicAddress,
broadcastPacket,
numHeaderBytes + numAssignmentBytes);
// remove the assignment from the queue
assignmentQueue.erase(assignment);
// stop looping, we've handed out an assignment
break;
}
// add the node ID to the end of the pointer
currentBufferPos += packNodeId(currentBufferPos, newNode->getNodeID());
// send the constructed list back to this node
nodeList->getNodeSocket()->send(destinationSocket,
broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes);
}
}
// if ASSIGNMENT_REQUEST_INTERVAL_USECS have passed since last global assignment request then fire off another
if (usecTimestampNow() - usecTimestamp(&lastGlobalAssignmentRequest) >= GLOBAL_ASSIGNMENT_REQUEST_INTERVAL_USECS) {
gettimeofday(&lastGlobalAssignmentRequest, NULL);
// go through our queue and see if there are any assignments to send to the global assignment server
std::deque<Assignment*>::iterator assignment = assignmentQueue.begin();
while (assignment != assignmentQueue.end()) {
if ((*assignment)->getLocation() != Assignment::LocalLocation) {
// attach our local socket to the assignment so the assignment-server can optionally hand it out
(*assignment)->setAttachedLocalSocket((sockaddr*) &localSocket);
nodeList->sendAssignment(*(*assignment));
// remove the assignment from the queue
assignmentQueue.erase(assignment);
// stop looping, we've handed out an assignment
break;
} else {
// push forward the iterator to check the next assignment
assignment++;
}
}
}
if (Logging::shouldSendStats()) {
if (usecTimestampNow() - usecTimestamp(&lastStatSendTime) >= (NODE_COUNT_STAT_INTERVAL_MSECS * 1000)) {
// time to send our count of nodes and servers to logstash
@ -253,9 +314,6 @@ int main(int argc, const char* argv[]) {
}
}
}
delete audioAssignment;
delete avatarAssignment;
return 0;
}

5383
domain-server/src/mongoose.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,384 @@
// Copyright (c) 2004-2012 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#include <stdio.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
struct mg_context; // Handle for the HTTP service itself
struct mg_connection; // Handle for the individual connection
// This structure contains information about the HTTP request.
struct mg_request_info {
const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL
const char *remote_user; // Authenticated user, or NULL if no auth used
long remote_ip; // Client's IP address
int remote_port; // Client's port
int is_ssl; // 1 if SSL-ed, 0 if not
void *user_data; // User data pointer passed to mg_start()
int num_headers; // Number of HTTP headers
struct mg_header {
const char *name; // HTTP header name
const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers
};
// This structure needs to be passed to mg_start(), to let mongoose know
// which callbacks to invoke. For detailed description, see
// https://github.com/valenok/mongoose/blob/master/UserManual.md
struct mg_callbacks {
// Called when mongoose has received new HTTP request.
// If callback returns non-zero,
// callback must process the request by sending valid HTTP headers and body,
// and mongoose will not do any further processing.
// If callback returns 0, mongoose processes the request itself. In this case,
// callback must not send any data to the client.
int (*begin_request)(struct mg_connection *);
// Called when mongoose has finished processing request.
void (*end_request)(const struct mg_connection *, int reply_status_code);
// Called when mongoose is about to log a message. If callback returns
// non-zero, mongoose does not log anything.
int (*log_message)(const struct mg_connection *, const char *message);
// Called when mongoose initializes SSL library.
int (*init_ssl)(void *ssl_context, void *user_data);
// Called when websocket request is received, before websocket handshake.
// If callback returns 0, mongoose proceeds with handshake, otherwise
// cinnection is closed immediately.
int (*websocket_connect)(const struct mg_connection *);
// Called when websocket handshake is successfully completed, and
// connection is ready for data exchange.
void (*websocket_ready)(struct mg_connection *);
// Called when data frame has been received from the client.
// Parameters:
// bits: first byte of the websocket frame, see websocket RFC at
// http://tools.ietf.org/html/rfc6455, section 5.2
// data, data_len: payload, with mask (if any) already applied.
// Return value:
// non-0: keep this websocket connection opened.
// 0: close this websocket connection.
int (*websocket_data)(struct mg_connection *, int bits,
char *data, size_t data_len);
// Called when mongoose tries to open a file. Used to intercept file open
// calls, and serve file data from memory instead.
// Parameters:
// path: Full path to the file to open.
// data_len: Placeholder for the file size, if file is served from memory.
// Return value:
// NULL: do not serve file from memory, proceed with normal file open.
// non-NULL: pointer to the file contents in memory. data_len must be
// initilized with the size of the memory block.
const char * (*open_file)(const struct mg_connection *,
const char *path, size_t *data_len);
// Called when mongoose is about to serve Lua server page (.lp file), if
// Lua support is enabled.
// Parameters:
// lua_context: "lua_State *" pointer.
void (*init_lua)(struct mg_connection *, void *lua_context);
// Called when mongoose has uploaded a file to a temporary directory as a
// result of mg_upload() call.
// Parameters:
// file_file: full path name to the uploaded file.
void (*upload)(struct mg_connection *, const char *file_name);
// Called when mongoose is about to send HTTP error to the client.
// Implementing this callback allows to create custom error pages.
// Parameters:
// status: HTTP error status code.
int (*http_error)(struct mg_connection *, int status);
};
// Start web server.
//
// Parameters:
// callbacks: mg_callbacks structure with user-defined callbacks.
// options: NULL terminated list of option_name, option_value pairs that
// specify Mongoose configuration parameters.
//
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
// processing is required for these, signal handlers must be set up
// after calling mg_start().
//
//
// Example:
// const char *options[] = {
// "document_root", "/var/www",
// "listening_ports", "80,443s",
// NULL
// };
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
//
// Refer to https://github.com/valenok/mongoose/blob/master/UserManual.md
// for the list of valid option and their possible values.
//
// Return:
// web server context, or NULL on error.
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
// Stop the web server.
//
// Must be called last, when an application wants to stop the web server and
// release all associated resources. This function blocks until all Mongoose
// threads are stopped. Context pointer becomes invalid.
void mg_stop(struct mg_context *);
// Get the value of particular configuration parameter.
// The value returned is read-only. Mongoose does not allow changing
// configuration at run time.
// If given parameter name is not valid, NULL is returned. For valid
// names, return value is guaranteed to be non-NULL. If parameter is not
// set, zero-length string is returned.
const char *mg_get_option(const struct mg_context *ctx, const char *name);
// Return array of strings that represent valid configuration options.
// For each option, a short name, long name, and default value is returned.
// Array is NULL terminated.
const char **mg_get_valid_option_names(void);
// Add, edit or delete the entry in the passwords file.
//
// This function allows an application to manipulate .htpasswd files on the
// fly by adding, deleting and changing user records. This is one of the
// several ways of implementing authentication on the server side. For another,
// cookie-based way please refer to the examples/chat.c in the source tree.
//
// If password is not NULL, entry is added (or modified if already exists).
// If password is NULL, entry is deleted.
//
// Return:
// 1 on success, 0 on error.
int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
// Return information associated with the request.
struct mg_request_info *mg_get_request_info(struct mg_connection *);
// Send data to the client.
// Return:
// 0 when the connection has been closed
// -1 on error
// >0 number of bytes written on success
int mg_write(struct mg_connection *, const void *buf, size_t len);
// Send data to a websocket client wrapped in a websocket frame.
// It is unsafe to read/write to this connection from another thread.
// This function is available when mongoose is compiled with -DUSE_WEBSOCKET
//
// Return:
// 0 when the connection has been closed
// -1 on error
// >0 number of bytes written on success
int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len);
// Opcodes, from http://tools.ietf.org/html/rfc6455
enum {
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
WEBSOCKET_OPCODE_TEXT = 0x1,
WEBSOCKET_OPCODE_BINARY = 0x2,
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
WEBSOCKET_OPCODE_PING = 0x9,
WEBSOCKET_OPCODE_PONG = 0xa
};
// Macros for enabling compiler-specific checks for printf-like arguments.
#undef PRINTF_FORMAT_STRING
#if _MSC_VER >= 1400
#include <sal.h>
#if _MSC_VER > 1400
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
#else
#define PRINTF_FORMAT_STRING(s) __format_string s
#endif
#else
#define PRINTF_FORMAT_STRING(s) s
#endif
#ifdef __GNUC__
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
#else
#define PRINTF_ARGS(x, y)
#endif
// Send data to the client using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
int mg_printf(struct mg_connection *,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
// Send contents of the entire file together with HTTP headers.
void mg_send_file(struct mg_connection *conn, const char *path);
// Read data from the remote end, return number of bytes read.
// Return:
// 0 connection has been closed by peer. No more data could be read.
// < 0 read error. No more data could be read from the connection.
// > 0 number of bytes read into the buffer.
int mg_read(struct mg_connection *, void *buf, size_t len);
// Get the value of particular HTTP header.
//
// This is a helper function. It traverses request_info->http_headers array,
// and if the header is present in the array, returns its value. If it is
// not present, NULL is returned.
const char *mg_get_header(const struct mg_connection *, const char *name);
// Get a value of particular form variable.
//
// Parameters:
// data: pointer to form-uri-encoded buffer. This could be either POST data,
// or request_info.query_string.
// data_len: length of the encoded data.
// var_name: variable name to decode from the buffer
// dst: destination buffer for the decoded variable
// dst_len: length of the destination buffer
//
// Return:
// On success, length of the decoded variable.
// On error:
// -1 (variable not found).
// -2 (destination buffer is NULL, zero length or too small to hold the
// decoded variable).
//
// Destination buffer is guaranteed to be '\0' - terminated if it is not
// NULL or zero length.
int mg_get_var(const char *data, size_t data_len,
const char *var_name, char *dst, size_t dst_len);
// Fetch value of certain cookie variable into the destination buffer.
//
// Destination buffer is guaranteed to be '\0' - terminated. In case of
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
// parameter. This function returns only first occurrence.
//
// Return:
// On success, value length.
// On error:
// -1 (either "Cookie:" header is not present at all or the requested
// parameter is not found).
// -2 (destination buffer is NULL, zero length or too small to hold the
// value).
int mg_get_cookie(const char *cookie, const char *var_name,
char *buf, size_t buf_len);
// Download data from the remote web server.
// host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
// port: port number, e.g. 80.
// use_ssl: wether to use SSL connection.
// error_buffer, error_buffer_size: error message placeholder.
// request_fmt,...: HTTP request.
// Return:
// On success, valid pointer to the new connection, suitable for mg_read().
// On error, NULL. error_buffer contains error message.
// Example:
// char ebuf[100];
// struct mg_connection *conn;
// conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
// "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
struct mg_connection *mg_download(const char *host, int port, int use_ssl,
char *error_buffer, size_t error_buffer_size,
PRINTF_FORMAT_STRING(const char *request_fmt),
...) PRINTF_ARGS(6, 7);
// Close the connection opened by mg_download().
void mg_close_connection(struct mg_connection *conn);
// File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent.
// Return number of uploaded files.
int mg_upload(struct mg_connection *conn, const char *destination_dir);
// Convenience function -- create detached thread.
// Return: 0 on success, non-0 on error.
typedef void * (*mg_thread_func_t)(void *);
int mg_start_thread(mg_thread_func_t f, void *p);
// Return builtin mime type for the given file name.
// For unrecognized extensions, "text/plain" is returned.
const char *mg_get_builtin_mime_type(const char *file_name);
// Return Mongoose version.
const char *mg_version(void);
// URL-decode input buffer into destination buffer.
// 0-terminate the destination buffer.
// form-url-encoded data differs from URI encoding in a way that it
// uses '+' as character for space, see RFC 1866 section 8.2.1
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
// Return: length of the decoded data, or -1 if dst buffer is too small.
int mg_url_decode(const char *src, int src_len, char *dst,
int dst_len, int is_form_url_encoded);
// MD5 hash given strings.
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
// ASCIIz strings. When function returns, buf will contain human-readable
// MD5 hash. Example:
// char buf[33];
// mg_md5(buf, "aa", "bb", NULL);
char *mg_md5(char buf[33], ...);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // MONGOOSE_HEADER_INCLUDED

View file

@ -0,0 +1,113 @@
body {
overflow: hidden;
background-color: #222;
}
#editor {
margin: 10px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.big-button {
font-size: 20px;
}
#deploy-button, #stop-button, #other-commands {
font-weight: bold;
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
z-index: 99;
top:30px;
display: inline-block;
position: absolute;
padding: 12px;
padding-left: 16px;
padding-right: 16px;
margin-right: 30px;
border-radius:6px;
animation: fadein 2s;
-moz-animation: fadein 2s; /* Firefox */
-webkit-animation: fadein 2s; /* Safari and Chrome */
-o-animation: fadein 2s; /* Opera */
}
#deploy-button {
background-color: #0DFFBB;
right: 0px;
}
#deploy-button:hover {
background-color: #28FF57;
}
#stop-button {
background-color: #CC1F00;
right: 0px;
}
#stop-button:hover {
background-color: #FF4114;
}
#other-commands {
right:176px;
text-align: right;
}
#other-commands ul {
margin: 0px;
margin-top: -10px;
}
#other-commands li {
list-style-type: none;
list-style: none;
margin-bottom: 2px;
}
#other-commands a,
#other-commands a:visited {
color:#eee;
text-decoration: none;
}
#other-commands a:hover {
color: #0DFFBB;
}
#deploy-button,#stop-button:hover,#other-commands:hover {
cursor: pointer;
}
#deploy-button a, #stop-button a {
text-decoration: none;
color:#222;
}
@keyframes fadein {
from {
opacity:0;
}
to {
opacity:1;
}
}
@-moz-keyframes fadein { /* Firefox */
from {
opacity:0;
}
to {
opacity:1;
}
}
@-webkit-keyframes fadein { /* Safari and Chrome */
from {
opacity:0;
}
to {
opacity:1;
}
}
@-o-keyframes fadein { /* Opera */
from {
opacity:0;
}
to {
opacity: 1;
}
}

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>Create JS Assignment</title>
<link href='css/style.css' rel='stylesheet'>
</head>
<body>
<pre id='editor' style='font-size: 14px;'><!--#include "placeholder.js"--></pre>
<script src='//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'></script>
<script src='//api.filepicker.io/v1/filepicker.js'></script>
<script src='js/ace/ace.js' type='text/javascript'></script>
<script src='js/assignment.js' type='text/javascript'></script>
<div class='big-button' id='deploy-button'>
<a href='#'>
Run
</a>
</div>
<!-- %div#stop-button.big-button -->
</body>
</html>
<!-- %a{:href => "#"} -->
<!-- Stop -->
<!-- %div#other-commands -->
<!-- %ul -->
<!-- %li -->
<!-- %a{:href => "#"} New Script -->
<!-- %li -->
<!-- %a{:href => "#"} Make a copy -->

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,888 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
ace.define('ace/mode/javascript', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text', 'ace/tokenizer', 'ace/mode/javascript_highlight_rules', 'ace/mode/matching_brace_outdent', 'ace/range', 'ace/worker/worker_client', 'ace/mode/behaviour/cstyle', 'ace/mode/folding/cstyle'], function(require, exports, module) {
var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var Tokenizer = require("../tokenizer").Tokenizer;
var JavaScriptHighlightRules = require("./javascript_highlight_rules").JavaScriptHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
var Range = require("../range").Range;
var WorkerClient = require("../worker/worker_client").WorkerClient;
var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
var Mode = function() {
var highlighter = new JavaScriptHighlightRules();
this.$tokenizer = new Tokenizer(highlighter.getRules());
this.$outdent = new MatchingBraceOutdent();
this.$behaviour = new CstyleBehaviour();
this.$keywordList = highlighter.$keywordList;
this.foldingRules = new CStyleFoldMode();
};
oop.inherits(Mode, TextMode);
(function() {
this.lineCommentStart = "//";
this.blockComment = {start: "/*", end: "*/"};
this.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
var tokenizedLine = this.$tokenizer.getLineTokens(line, state);
var tokens = tokenizedLine.tokens;
var endState = tokenizedLine.state;
if (tokens.length && tokens[tokens.length-1].type == "comment") {
return indent;
}
if (state == "start" || state == "no_regex") {
var match = line.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);
if (match) {
indent += tab;
}
} else if (state == "doc-start") {
if (endState == "start" || endState == "no_regex") {
return "";
}
var match = line.match(/^\s*(\/?)\*/);
if (match) {
if (match[1]) {
indent += " ";
}
indent += "* ";
}
}
return indent;
};
this.checkOutdent = function(state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function(state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
this.createWorker = function(session) {
var worker = new WorkerClient(["ace"], "ace/mode/javascript_worker", "JavaScriptWorker");
worker.attachToDocument(session.getDocument());
worker.on("jslint", function(results) {
session.setAnnotations(results.data);
});
worker.on("terminate", function() {
session.clearAnnotations();
});
return worker;
};
}).call(Mode.prototype);
exports.Mode = Mode;
});
ace.define('ace/mode/javascript_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/doc_comment_highlight_rules', 'ace/mode/text_highlight_rules'], function(require, exports, module) {
var oop = require("../lib/oop");
var DocCommentHighlightRules = require("./doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var JavaScriptHighlightRules = function() {
var keywordMapper = this.createKeywordMapper({
"variable.language":
"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|" + // Constructors
"Namespace|QName|XML|XMLList|" + // E4X
"ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|" +
"Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|" +
"Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|" + // Errors
"SyntaxError|TypeError|URIError|" +
"decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|" + // Non-constructor functions
"isNaN|parseFloat|parseInt|" +
"JSON|Math|" + // Other
"this|arguments|prototype|window|document" , // Pseudo
"keyword":
"const|yield|import|get|set|" +
"break|case|catch|continue|default|delete|do|else|finally|for|function|" +
"if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|" +
"__parent__|__count__|escape|unescape|with|__proto__|" +
"class|enum|extends|super|export|implements|private|public|interface|package|protected|static",
"storage.type":
"const|let|var|function",
"constant.language":
"null|Infinity|NaN|undefined",
"support.function":
"alert",
"constant.language.boolean": "true|false"
}, "identifier");
var kwBeforeRe = "case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void";
var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b";
var escapedRe = "\\\\(?:x[0-9a-fA-F]{2}|" + // hex
"u[0-9a-fA-F]{4}|" + // unicode
"[0-2][0-7]{0,2}|" + // oct
"3[0-6][0-7]?|" + // oct
"37[0-7]?|" + // oct
"[4-7][0-7]?|" + //oct
".)";
this.$rules = {
"no_regex" : [
{
token : "comment",
regex : "\\/\\/",
next : "line_comment"
},
DocCommentHighlightRules.getStartRule("doc-start"),
{
token : "comment", // multi line comment
regex : /\/\*/,
next : "comment"
}, {
token : "string",
regex : "'(?=.)",
next : "qstring"
}, {
token : "string",
regex : '"(?=.)',
next : "qqstring"
}, {
token : "constant.numeric", // hex
regex : /0[xX][0-9a-fA-F]+\b/
}, {
token : "constant.numeric", // float
regex : /[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/
}, {
token : [
"storage.type", "punctuation.operator", "support.function",
"punctuation.operator", "entity.name.function", "text","keyword.operator"
],
regex : "(" + identifierRe + ")(\\.)(prototype)(\\.)(" + identifierRe +")(\\s*)(=)",
next: "function_arguments"
}, {
token : [
"storage.type", "punctuation.operator", "entity.name.function", "text",
"keyword.operator", "text", "storage.type", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"entity.name.function", "text", "keyword.operator", "text", "storage.type",
"text", "paren.lparen"
],
regex : "(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"storage.type", "punctuation.operator", "entity.name.function", "text",
"keyword.operator", "text",
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
],
regex : "(function)(\\s+)(" + identifierRe + ")(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"entity.name.function", "text", "punctuation.operator",
"text", "storage.type", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"text", "text", "storage.type", "text", "paren.lparen"
],
regex : "(:)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : "keyword",
regex : "(?:" + kwBeforeRe + ")\\b",
next : "start"
}, {
token : ["punctuation.operator", "support.function"],
regex : /(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/
}, {
token : ["punctuation.operator", "support.function.dom"],
regex : /(\.)(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/
}, {
token : ["punctuation.operator", "support.constant"],
regex : /(\.)(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/
}, {
token : ["storage.type", "punctuation.operator", "support.function.firebug"],
regex : /(console)(\.)(warn|info|log|error|time|timeEnd|assert)\b/
}, {
token : keywordMapper,
regex : identifierRe
}, {
token : "keyword.operator",
regex : /--|\+\+|[!$%&*+\-~]|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\|\||\?\:|\*=|%=|\+=|\-=|&=|\^=/,
next : "start"
}, {
token : "punctuation.operator",
regex : /\?|\:|\,|\;|\./,
next : "start"
}, {
token : "paren.lparen",
regex : /[\[({]/,
next : "start"
}, {
token : "paren.rparen",
regex : /[\])}]/
}, {
token : "keyword.operator",
regex : /\/=?/,
next : "start"
}, {
token: "comment",
regex: /^#!.*$/
}
],
"start": [
DocCommentHighlightRules.getStartRule("doc-start"),
{
token : "comment", // multi line comment
regex : "\\/\\*",
next : "comment_regex_allowed"
}, {
token : "comment",
regex : "\\/\\/",
next : "line_comment_regex_allowed"
}, {
token: "string.regexp",
regex: "\\/",
next: "regex"
}, {
token : "text",
regex : "\\s+|^$",
next : "start"
}, {
token: "empty",
regex: "",
next: "no_regex"
}
],
"regex": [
{
token: "regexp.keyword.operator",
regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
}, {
token: "string.regexp",
regex: "/\\w*",
next: "no_regex"
}, {
token : "invalid",
regex: /\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/
}, {
token : "constant.language.escape",
regex: /\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?]/
}, {
token : "constant.language.delimiter",
regex: /\|/
}, {
token: "constant.language.escape",
regex: /\[\^?/,
next: "regex_character_class"
}, {
token: "empty",
regex: "$",
next: "no_regex"
}, {
defaultToken: "string.regexp"
}
],
"regex_character_class": [
{
token: "regexp.keyword.operator",
regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
}, {
token: "constant.language.escape",
regex: "]",
next: "regex"
}, {
token: "constant.language.escape",
regex: "-"
}, {
token: "empty",
regex: "$",
next: "no_regex"
}, {
defaultToken: "string.regexp.charachterclass"
}
],
"function_arguments": [
{
token: "variable.parameter",
regex: identifierRe
}, {
token: "punctuation.operator",
regex: "[, ]+"
}, {
token: "punctuation.operator",
regex: "$"
}, {
token: "empty",
regex: "",
next: "no_regex"
}
],
"comment_regex_allowed" : [
{token : "comment", regex : "\\*\\/", next : "start"},
{defaultToken : "comment"}
],
"comment" : [
{token : "comment", regex : "\\*\\/", next : "no_regex"},
{defaultToken : "comment"}
],
"line_comment_regex_allowed" : [
{token : "comment", regex : "$|^", next : "start"},
{defaultToken : "comment"}
],
"line_comment" : [
{token : "comment", regex : "$|^", next : "no_regex"},
{defaultToken : "comment"}
],
"qqstring" : [
{
token : "constant.language.escape",
regex : escapedRe
}, {
token : "string",
regex : "\\\\$",
next : "qqstring"
}, {
token : "string",
regex : '"|$',
next : "no_regex"
}, {
defaultToken: "string"
}
],
"qstring" : [
{
token : "constant.language.escape",
regex : escapedRe
}, {
token : "string",
regex : "\\\\$",
next : "qstring"
}, {
token : "string",
regex : "'|$",
next : "no_regex"
}, {
defaultToken: "string"
}
]
};
this.embedRules(DocCommentHighlightRules, "doc-",
[ DocCommentHighlightRules.getEndRule("no_regex") ]);
};
oop.inherits(JavaScriptHighlightRules, TextHighlightRules);
exports.JavaScriptHighlightRules = JavaScriptHighlightRules;
});
ace.define('ace/mode/doc_comment_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function(require, exports, module) {
var oop = require("../lib/oop");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var DocCommentHighlightRules = function() {
this.$rules = {
"start" : [ {
token : "comment.doc.tag",
regex : "@[\\w\\d_]+" // TODO: fix email addresses
}, {
token : "comment.doc.tag",
regex : "\\bTODO\\b"
}, {
defaultToken : "comment.doc"
}]
};
};
oop.inherits(DocCommentHighlightRules, TextHighlightRules);
DocCommentHighlightRules.getStartRule = function(start) {
return {
token : "comment.doc", // doc comment
regex : "\\/\\*(?=\\*)",
next : start
};
};
DocCommentHighlightRules.getEndRule = function (start) {
return {
token : "comment.doc", // closing comment
regex : "\\*\\/",
next : start
};
};
exports.DocCommentHighlightRules = DocCommentHighlightRules;
});
ace.define('ace/mode/matching_brace_outdent', ['require', 'exports', 'module' , 'ace/range'], function(require, exports, module) {
var Range = require("../range").Range;
var MatchingBraceOutdent = function() {};
(function() {
this.checkOutdent = function(line, input) {
if (! /^\s+$/.test(line))
return false;
return /^\s*\}/.test(input);
};
this.autoOutdent = function(doc, row) {
var line = doc.getLine(row);
var match = line.match(/^(\s*\})/);
if (!match) return 0;
var column = match[1].length;
var openBracePos = doc.findMatchingBracket({row: row, column: column});
if (!openBracePos || openBracePos.row == row) return 0;
var indent = this.$getIndent(doc.getLine(openBracePos.row));
doc.replace(new Range(row, 0, row, column-1), indent);
};
this.$getIndent = function(line) {
return line.match(/^\s*/)[0];
};
}).call(MatchingBraceOutdent.prototype);
exports.MatchingBraceOutdent = MatchingBraceOutdent;
});
ace.define('ace/mode/behaviour/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/behaviour', 'ace/token_iterator', 'ace/lib/lang'], function(require, exports, module) {
var oop = require("../../lib/oop");
var Behaviour = require("../behaviour").Behaviour;
var TokenIterator = require("../../token_iterator").TokenIterator;
var lang = require("../../lib/lang");
var SAFE_INSERT_IN_TOKENS =
["text", "paren.rparen", "punctuation.operator"];
var SAFE_INSERT_BEFORE_TOKENS =
["text", "paren.rparen", "punctuation.operator", "comment"];
var autoInsertedBrackets = 0;
var autoInsertedRow = -1;
var autoInsertedLineEnd = "";
var maybeInsertedBrackets = 0;
var maybeInsertedRow = -1;
var maybeInsertedLineStart = "";
var maybeInsertedLineEnd = "";
var CstyleBehaviour = function () {
CstyleBehaviour.isSaneInsertion = function(editor, session) {
var cursor = editor.getCursorPosition();
var iterator = new TokenIterator(session, cursor.row, cursor.column);
if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) {
var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1);
if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS))
return false;
}
iterator.stepForward();
return iterator.getCurrentTokenRow() !== cursor.row ||
this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS);
};
CstyleBehaviour.$matchTokenType = function(token, types) {
return types.indexOf(token.type || token) > -1;
};
CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
if (!this.isAutoInsertedClosing(cursor, line, autoInsertedLineEnd[0]))
autoInsertedBrackets = 0;
autoInsertedRow = cursor.row;
autoInsertedLineEnd = bracket + line.substr(cursor.column);
autoInsertedBrackets++;
};
CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
if (!this.isMaybeInsertedClosing(cursor, line))
maybeInsertedBrackets = 0;
maybeInsertedRow = cursor.row;
maybeInsertedLineStart = line.substr(0, cursor.column) + bracket;
maybeInsertedLineEnd = line.substr(cursor.column);
maybeInsertedBrackets++;
};
CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) {
return autoInsertedBrackets > 0 &&
cursor.row === autoInsertedRow &&
bracket === autoInsertedLineEnd[0] &&
line.substr(cursor.column) === autoInsertedLineEnd;
};
CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) {
return maybeInsertedBrackets > 0 &&
cursor.row === maybeInsertedRow &&
line.substr(cursor.column) === maybeInsertedLineEnd &&
line.substr(0, cursor.column) == maybeInsertedLineStart;
};
CstyleBehaviour.popAutoInsertedClosing = function() {
autoInsertedLineEnd = autoInsertedLineEnd.substr(1);
autoInsertedBrackets--;
};
CstyleBehaviour.clearMaybeInsertedClosing = function() {
maybeInsertedBrackets = 0;
maybeInsertedRow = -1;
};
this.add("braces", "insertion", function (state, action, editor, session, text) {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
if (text == '{') {
var selection = editor.getSelectionRange();
var selected = session.doc.getTextRange(selection);
if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) {
return {
text: '{' + selected + '}',
selection: false
};
} else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
if (/[\]\}\)]/.test(line[cursor.column])) {
CstyleBehaviour.recordAutoInsert(editor, session, "}");
return {
text: '{}',
selection: [1, 1]
};
} else {
CstyleBehaviour.recordMaybeInsert(editor, session, "{");
return {
text: '{',
selection: [1, 1]
};
}
}
} else if (text == '}') {
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar == '}') {
var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row});
if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
CstyleBehaviour.popAutoInsertedClosing();
return {
text: '',
selection: [1, 1]
};
}
}
} else if (text == "\n" || text == "\r\n") {
var closing = "";
if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) {
closing = lang.stringRepeat("}", maybeInsertedBrackets);
CstyleBehaviour.clearMaybeInsertedClosing();
}
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar == '}' || closing !== "") {
var openBracePos = session.findMatchingBracket({row: cursor.row, column: cursor.column}, '}');
if (!openBracePos)
return null;
var indent = this.getNextLineIndent(state, line.substring(0, cursor.column), session.getTabString());
var next_indent = this.$getIndent(line);
return {
text: '\n' + indent + '\n' + next_indent + closing,
selection: [1, indent.length, 1, indent.length]
};
}
}
});
this.add("braces", "deletion", function (state, action, editor, session, range) {
var selected = session.doc.getTextRange(range);
if (!range.isMultiLine() && selected == '{') {
var line = session.doc.getLine(range.start.row);
var rightChar = line.substring(range.end.column, range.end.column + 1);
if (rightChar == '}') {
range.end.column++;
return range;
} else {
maybeInsertedBrackets--;
}
}
});
this.add("parens", "insertion", function (state, action, editor, session, text) {
if (text == '(') {
var selection = editor.getSelectionRange();
var selected = session.doc.getTextRange(selection);
if (selected !== "" && editor.getWrapBehavioursEnabled()) {
return {
text: '(' + selected + ')',
selection: false
};
} else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
CstyleBehaviour.recordAutoInsert(editor, session, ")");
return {
text: '()',
selection: [1, 1]
};
}
} else if (text == ')') {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar == ')') {
var matching = session.$findOpeningBracket(')', {column: cursor.column + 1, row: cursor.row});
if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
CstyleBehaviour.popAutoInsertedClosing();
return {
text: '',
selection: [1, 1]
};
}
}
}
});
this.add("parens", "deletion", function (state, action, editor, session, range) {
var selected = session.doc.getTextRange(range);
if (!range.isMultiLine() && selected == '(') {
var line = session.doc.getLine(range.start.row);
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
if (rightChar == ')') {
range.end.column++;
return range;
}
}
});
this.add("brackets", "insertion", function (state, action, editor, session, text) {
if (text == '[') {
var selection = editor.getSelectionRange();
var selected = session.doc.getTextRange(selection);
if (selected !== "" && editor.getWrapBehavioursEnabled()) {
return {
text: '[' + selected + ']',
selection: false
};
} else if (CstyleBehaviour.isSaneInsertion(editor, session)) {
CstyleBehaviour.recordAutoInsert(editor, session, "]");
return {
text: '[]',
selection: [1, 1]
};
}
} else if (text == ']') {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar == ']') {
var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row});
if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) {
CstyleBehaviour.popAutoInsertedClosing();
return {
text: '',
selection: [1, 1]
};
}
}
}
});
this.add("brackets", "deletion", function (state, action, editor, session, range) {
var selected = session.doc.getTextRange(range);
if (!range.isMultiLine() && selected == '[') {
var line = session.doc.getLine(range.start.row);
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
if (rightChar == ']') {
range.end.column++;
return range;
}
}
});
this.add("string_dquotes", "insertion", function (state, action, editor, session, text) {
if (text == '"' || text == "'") {
var quote = text;
var selection = editor.getSelectionRange();
var selected = session.doc.getTextRange(selection);
if (selected !== "" && selected !== "'" && selected != '"' && editor.getWrapBehavioursEnabled()) {
return {
text: quote + selected + quote,
selection: false
};
} else {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
var leftChar = line.substring(cursor.column-1, cursor.column);
if (leftChar == '\\') {
return null;
}
var tokens = session.getTokens(selection.start.row);
var col = 0, token;
var quotepos = -1; // Track whether we're inside an open quote.
for (var x = 0; x < tokens.length; x++) {
token = tokens[x];
if (token.type == "string") {
quotepos = -1;
} else if (quotepos < 0) {
quotepos = token.value.indexOf(quote);
}
if ((token.value.length + col) > selection.start.column) {
break;
}
col += tokens[x].value.length;
}
if (!token || (quotepos < 0 && token.type !== "comment" && (token.type !== "string" || ((selection.start.column !== token.value.length+col-1) && token.value.lastIndexOf(quote) === token.value.length-1)))) {
if (!CstyleBehaviour.isSaneInsertion(editor, session))
return;
return {
text: quote + quote,
selection: [1,1]
};
} else if (token && token.type === "string") {
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar == quote) {
return {
text: '',
selection: [1, 1]
};
}
}
}
}
});
this.add("string_dquotes", "deletion", function (state, action, editor, session, range) {
var selected = session.doc.getTextRange(range);
if (!range.isMultiLine() && (selected == '"' || selected == "'")) {
var line = session.doc.getLine(range.start.row);
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
if (rightChar == selected) {
range.end.column++;
return range;
}
}
});
};
oop.inherits(CstyleBehaviour, Behaviour);
exports.CstyleBehaviour = CstyleBehaviour;
});
ace.define('ace/mode/folding/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/range', 'ace/mode/folding/fold_mode'], function(require, exports, module) {
var oop = require("../../lib/oop");
var Range = require("../../range").Range;
var BaseFoldMode = require("./fold_mode").FoldMode;
var FoldMode = exports.FoldMode = function(commentRegex) {
if (commentRegex) {
this.foldingStartMarker = new RegExp(
this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
);
this.foldingStopMarker = new RegExp(
this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
);
}
};
oop.inherits(FoldMode, BaseFoldMode);
(function() {
this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
this.getFoldWidgetRange = function(session, foldStyle, row) {
var line = session.getLine(row);
var match = line.match(this.foldingStartMarker);
if (match) {
var i = match.index;
if (match[1])
return this.openingBracketBlock(session, match[1], row, i);
return session.getCommentFoldRange(row, i + match[0].length, 1);
}
if (foldStyle !== "markbeginend")
return;
var match = line.match(this.foldingStopMarker);
if (match) {
var i = match.index + match[0].length;
if (match[1])
return this.closingBracketBlock(session, match[1], row, i);
return session.getCommentFoldRange(row, i, -1);
}
};
}).call(FoldMode.prototype);
});

View file

@ -0,0 +1,202 @@
ace.define('ace/snippets/javascript', ['require', 'exports', 'module' ], function(require, exports, module) {
exports.snippetText = "# Prototype\n\
snippet proto\n\
${1:class_name}.prototype.${2:method_name} = function(${3:first_argument}) {\n\
${4:// body...}\n\
};\n\
# Function\n\
snippet fun\n\
function ${1?:function_name}(${2:argument}) {\n\
${3:// body...}\n\
}\n\
# Anonymous Function\n\
regex /((=)\\s*|(:)\\s*|(\\()|\\b)/f/(\\))?/\n\
name f\n\
function${M1?: ${1:functionName}}($2) {\n\
${0:$TM_SELECTED_TEXT}\n\
}${M2?;}${M3?,}${M4?)}\n\
# Immediate function\n\
trigger \\(?f\\(\n\
endTrigger \\)?\n\
snippet f(\n\
(function(${1}) {\n\
${0:${TM_SELECTED_TEXT:/* code */}}\n\
}(${1}));\n\
# if\n\
snippet if\n\
if (${1:true}) {\n\
${0}\n\
}\n\
# if ... else\n\
snippet ife\n\
if (${1:true}) {\n\
${2}\n\
} else {\n\
${0}\n\
}\n\
# tertiary conditional\n\
snippet ter\n\
${1:/* condition */} ? ${2:a} : ${3:b}\n\
# switch\n\
snippet switch\n\
switch (${1:expression}) {\n\
case '${3:case}':\n\
${4:// code}\n\
break;\n\
${5}\n\
default:\n\
${2:// code}\n\
}\n\
# case\n\
snippet case\n\
case '${1:case}':\n\
${2:// code}\n\
break;\n\
${3}\n\
\n\
# while (...) {...}\n\
snippet wh\n\
while (${1:/* condition */}) {\n\
${0:/* code */}\n\
}\n\
# try\n\
snippet try\n\
try {\n\
${0:/* code */}\n\
} catch (e) {}\n\
# do...while\n\
snippet do\n\
do {\n\
${2:/* code */}\n\
} while (${1:/* condition */});\n\
# Object Method\n\
snippet :f\n\
regex /([,{[])|^\\s*/:f/\n\
${1:method_name}: function(${2:attribute}) {\n\
${0}\n\
}${3:,}\n\
# setTimeout function\n\
snippet setTimeout\n\
regex /\\b/st|timeout|setTimeo?u?t?/\n\
setTimeout(function() {${3:$TM_SELECTED_TEXT}}, ${1:10});\n\
# Get Elements\n\
snippet gett\n\
getElementsBy${1:TagName}('${2}')${3}\n\
# Get Element\n\
snippet get\n\
getElementBy${1:Id}('${2}')${3}\n\
# console.log (Firebug)\n\
snippet cl\n\
console.log(${1});\n\
# return\n\
snippet ret\n\
return ${1:result}\n\
# for (property in object ) { ... }\n\
snippet fori\n\
for (var ${1:prop} in ${2:Things}) {\n\
${0:$2[$1]}\n\
}\n\
# hasOwnProperty\n\
snippet has\n\
hasOwnProperty(${1})\n\
# docstring\n\
snippet /**\n\
/**\n\
* ${1:description}\n\
*\n\
*/\n\
snippet @par\n\
regex /^\\s*\\*\\s*/@(para?m?)?/\n\
@param {${1:type}} ${2:name} ${3:description}\n\
snippet @ret\n\
@return {${1:type}} ${2:description}\n\
# JSON.parse\n\
snippet jsonp\n\
JSON.parse(${1:jstr});\n\
# JSON.stringify\n\
snippet jsons\n\
JSON.stringify(${1:object});\n\
# self-defining function\n\
snippet sdf\n\
var ${1:function_name} = function(${2:argument}) {\n\
${3:// initial code ...}\n\
\n\
$1 = function($2) {\n\
${4:// main code}\n\
};\n\
}\n\
# singleton\n\
snippet sing\n\
function ${1:Singleton} (${2:argument}) {\n\
// the cached instance\n\
var instance;\n\
\n\
// rewrite the constructor\n\
$1 = function $1($2) {\n\
return instance;\n\
};\n\
\n\
// carry over the prototype properties\n\
$1.prototype = this;\n\
\n\
// the instance\n\
instance = new $1();\n\
\n\
// reset the constructor pointer\n\
instance.constructor = $1;\n\
\n\
${3:// code ...}\n\
\n\
return instance;\n\
}\n\
# class\n\
name class\n\
regex /^\\s*/clas{0,2}/\n\
var ${1:class} = function(${20}) {\n\
$40$0\n\
};\n\
\n\
(function() {\n\
${60:this.prop = \"\"}\n\
}).call(${1:class}.prototype);\n\
\n\
exports.${1:class} = ${1:class};\n\
# \n\
snippet for-\n\
for (var ${1:i} = ${2:Things}.length; ${1:i}--; ) {\n\
${0:${2:Things}[${1:i}];}\n\
}\n\
# for (...) {...}\n\
snippet for\n\
for (var ${1:i} = 0; $1 < ${2:Things}.length; $1++) {\n\
${3:$2[$1]}$0\n\
}\n\
# for (...) {...} (Improved Native For-Loop)\n\
snippet forr\n\
for (var ${1:i} = ${2:Things}.length - 1; $1 >= 0; $1--) {\n\
${3:$2[$1]}$0\n\
}\n\
\n\
\n\
#modules\n\
snippet def\n\
ace.define(function(require, exports, module) {\n\
\"use strict\";\n\
var ${1/.*\\///} = require(\"${1}\");\n\
\n\
$TM_SELECTED_TEXT\n\
});\n\
snippet req\n\
guard ^\\s*\n\
var ${1/.*\\///} = require(\"${1}\");\n\
$0\n\
snippet requ\n\
guard ^\\s*\n\
var ${1/.*\\/(.)/\\u$1/} = require(\"${1}\").${1/.*\\/(.)/\\u$1/};\n\
$0\n\
";
exports.scope = "javascript";
});

View file

@ -0,0 +1,142 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
ace.define('ace/theme/twilight', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
exports.isDark = true;
exports.cssClass = "ace-twilight";
exports.cssText = ".ace-twilight .ace_gutter {\
background: #232323;\
color: #E2E2E2\
}\
.ace-twilight .ace_print-margin {\
width: 1px;\
background: #232323\
}\
.ace-twilight {\
background-color: #141414;\
color: #F8F8F8\
}\
.ace-twilight .ace_cursor {\
border-left: 2px solid #A7A7A7\
}\
.ace-twilight .ace_overwrite-cursors .ace_cursor {\
border-left: 0px;\
border-bottom: 1px solid #A7A7A7\
}\
.ace-twilight .ace_marker-layer .ace_selection {\
background: rgba(221, 240, 255, 0.20)\
}\
.ace-twilight.ace_multiselect .ace_selection.ace_start {\
box-shadow: 0 0 3px 0px #141414;\
border-radius: 2px\
}\
.ace-twilight .ace_marker-layer .ace_step {\
background: rgb(102, 82, 0)\
}\
.ace-twilight .ace_marker-layer .ace_bracket {\
margin: -1px 0 0 -1px;\
border: 1px solid rgba(255, 255, 255, 0.25)\
}\
.ace-twilight .ace_marker-layer .ace_active-line {\
background: rgba(255, 255, 255, 0.031)\
}\
.ace-twilight .ace_gutter-active-line {\
background-color: rgba(255, 255, 255, 0.031)\
}\
.ace-twilight .ace_marker-layer .ace_selected-word {\
border: 1px solid rgba(221, 240, 255, 0.20)\
}\
.ace-twilight .ace_invisible {\
color: rgba(255, 255, 255, 0.25)\
}\
.ace-twilight .ace_keyword,\
.ace-twilight .ace_meta {\
color: #CDA869\
}\
.ace-twilight .ace_constant,\
.ace-twilight .ace_constant.ace_character,\
.ace-twilight .ace_constant.ace_character.ace_escape,\
.ace-twilight .ace_constant.ace_other,\
.ace-twilight .ace_heading,\
.ace-twilight .ace_support.ace_constant {\
color: #CF6A4C\
}\
.ace-twilight .ace_invalid.ace_illegal {\
color: #F8F8F8;\
background-color: rgba(86, 45, 86, 0.75)\
}\
.ace-twilight .ace_invalid.ace_deprecated {\
text-decoration: underline;\
font-style: italic;\
color: #D2A8A1\
}\
.ace-twilight .ace_support {\
color: #9B859D\
}\
.ace-twilight .ace_fold {\
background-color: #AC885B;\
border-color: #F8F8F8\
}\
.ace-twilight .ace_support.ace_function {\
color: #DAD085\
}\
.ace-twilight .ace_list,\
.ace-twilight .ace_storage {\
color: #F9EE98\
}\
.ace-twilight .ace_entity.ace_name.ace_function,\
.ace-twilight .ace_meta.ace_tag,\
.ace-twilight .ace_variable {\
color: #AC885B\
}\
.ace-twilight .ace_string {\
color: #8F9D6A\
}\
.ace-twilight .ace_string.ace_regexp {\
color: #E9C062\
}\
.ace-twilight .ace_comment {\
font-style: italic;\
color: #5F5A60\
}\
.ace-twilight .ace_variable {\
color: #7587A6\
}\
.ace-twilight .ace_xml-pe {\
color: #494949\
}\
.ace-twilight .ace_indent-guide {\
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWMQERFpYLC1tf0PAAgOAnPnhxyiAAAAAElFTkSuQmCC) right repeat-y;\
}";
var dom = require("../lib/dom");
dom.importCssString(exports.cssText, exports.cssClass);
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
$(document).ready(function(){
// setup the editor
var editor = ace.edit("editor");
editor.setTheme("ace/theme/twilight")
editor.getSession().setMode("ace/mode/javascript");
editor.getSession().setTabSize(2);
// setup the Ink filepicker
filepicker.setKey("ARhz9KegMS0ioo5o9bPOcz");
$('#deploy-button').click(function(){
script = editor.getValue();
// store the script on S3 using filepicker
filepicker.store(script, {mimetype: 'application/javascript'}, function(blob){
console.log(JSON.stringify(blob));
s3_filename = blob["key"];
$.post('/assignment', {s3_filename: s3_filename}, function(response){
// the response is the assignment ID, if successful
console.log(response);
}, function(error) {
console.log(error);
});
}, function(FPError){
console.log(FPError);
});
});
});

View file

@ -0,0 +1,20 @@
/* Add your JavaScript for assignment below this line */
// here are some examples of things you can call
Avatar.position = {x: 0, y: 0.565925, z: 10};
Avatar.chatMessage = "I am not a robot!";
Avatar.handPosition = {x: 0, y: 4.5, z: 0};
// here I'm creating a function to fire before each data send
function dance() {
// switch the body yaw from 1 to 90
var randomAngle = Math.floor(Math.random() * 90);
if (Math.random() < 0.5) {
randomAngle * -1;
}
Avatar.bodyYaw = randomAngle;
}
// register the call back so it fires before each data send
Agent.preSendCallback.connect(dance);

View file

@ -0,0 +1 @@
If you can see this, your domain-server is alive and kicking. Go explore!

View file

@ -370,11 +370,11 @@ Menu::Menu() :
SLOT(setDepthOnly(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu,
MenuOption::Faceshift,
MenuOption::FaceshiftTCP,
0,
false,
appInstance->getFaceshift(),
SLOT(setEnabled(bool)));
SLOT(setTCPEnabled(bool)));
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio);

View file

@ -137,7 +137,7 @@ namespace MenuOption {
const QString ExportVoxels = "Export Voxels";
const QString HeadMouse = "Head Mouse";
const QString FaceMode = "Cycle Face Mode";
const QString Faceshift = "Faceshift";
const QString FaceshiftTCP = "Faceshift (TCP)";
const QString FalseColorByDistance = "FALSE Color By Distance";
const QString FalseColorBySource = "FALSE Color By Source";
const QString FalseColorEveryOtherVoxel = "FALSE Color Every Other Randomly";

View file

@ -661,21 +661,22 @@ void Head::renderEyeBalls() {
glm::vec3 front = orientation * IDENTITY_FRONT;
// render left iris
glm::quat leftIrisRotation;
glPushMatrix(); {
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _leftEyePosition;
glm::quat rotation = rotationBetween(front, targetLookatVector) * orientation;
glm::vec3 rotationAxis = glm::axis(rotation);
glRotatef(glm::angle(rotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
leftIrisRotation = rotationBetween(front, targetLookatVector) * orientation;
glm::vec3 rotationAxis = glm::axis(leftIrisRotation);
glRotatef(glm::angle(leftIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION);
glScalef(_scale * IRIS_RADIUS * 2.0f,
_scale * IRIS_RADIUS * 2.0f,
_scale * IRIS_RADIUS); // flatten the iris
// this ugliness is simply to invert the model transform and get the eye position in model space
_irisProgram.setUniform(_eyePositionLocation, (glm::inverse(rotation) *
_irisProgram.setUniform(_eyePositionLocation, (glm::inverse(leftIrisRotation) *
(Application::getInstance()->getCamera()->getPosition() - _leftEyePosition) +
glm::vec3(0.0f, 0.0f, _scale * IRIS_PROTRUSION)) * glm::vec3(1.0f / (_scale * IRIS_RADIUS * 2.0f),
1.0f / (_scale * IRIS_RADIUS * 2.0f), 1.0f / (_scale * IRIS_RADIUS)));
@ -685,21 +686,22 @@ void Head::renderEyeBalls() {
glPopMatrix();
// render right iris
glm::quat rightIrisRotation;
glPushMatrix(); {
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _rightEyePosition;
glm::quat rotation = rotationBetween(front, targetLookatVector) * orientation;
glm::vec3 rotationAxis = glm::axis(rotation);
glRotatef(glm::angle(rotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
rightIrisRotation = rotationBetween(front, targetLookatVector) * orientation;
glm::vec3 rotationAxis = glm::axis(rightIrisRotation);
glRotatef(glm::angle(rightIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION);
glScalef(_scale * IRIS_RADIUS * 2.0f,
_scale * IRIS_RADIUS * 2.0f,
_scale * IRIS_RADIUS); // flatten the iris
// this ugliness is simply to invert the model transform and get the eye position in model space
_irisProgram.setUniform(_eyePositionLocation, (glm::inverse(rotation) *
_irisProgram.setUniform(_eyePositionLocation, (glm::inverse(rightIrisRotation) *
(Application::getInstance()->getCamera()->getPosition() - _rightEyePosition) +
glm::vec3(0.0f, 0.0f, _scale * IRIS_PROTRUSION)) * glm::vec3(1.0f / (_scale * IRIS_RADIUS * 2.0f),
1.0f / (_scale * IRIS_RADIUS * 2.0f), 1.0f / (_scale * IRIS_RADIUS)));
@ -718,12 +720,13 @@ void Head::renderEyeBalls() {
// left eyelid
glPushMatrix(); {
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
glm::vec3 rotationAxis = glm::axis(orientation);
glRotatef(glm::angle(orientation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glm::vec3 rotationAxis = glm::axis(leftIrisRotation);
glRotatef(glm::angle(leftIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glScalef(_scale * EYELID_RADIUS, _scale * EYELID_RADIUS, _scale * EYELID_RADIUS);
glRotatef(-40 - 50 * _leftEyeBlink, 1, 0, 0);
float angle = -67.5f - 50.0f * _leftEyeBlink;
glRotatef(angle, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
glRotatef(180 * _leftEyeBlink, 1, 0, 0);
glRotatef(glm::mix(-angle, 180.0f, max(0.0f, _leftEyeBlink)), 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
}
glPopMatrix();
@ -731,12 +734,13 @@ void Head::renderEyeBalls() {
// right eyelid
glPushMatrix(); {
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
glm::vec3 rotationAxis = glm::axis(orientation);
glRotatef(glm::angle(orientation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glm::vec3 rotationAxis = glm::axis(rightIrisRotation);
glRotatef(glm::angle(rightIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glScalef(_scale * EYELID_RADIUS, _scale * EYELID_RADIUS, _scale * EYELID_RADIUS);
glRotatef(-40 - 50 * _rightEyeBlink, 1, 0, 0);
float angle = -67.5f - 50.0f * _rightEyeBlink;
glRotatef(angle, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
glRotatef(180 * _rightEyeBlink, 1, 0, 0);
glRotatef(glm::mix(-angle, 180.0f, max(0.0f, _rightEyeBlink)), 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
}
glPopMatrix();

View file

@ -347,14 +347,19 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
} else if (webcam->isActive()) {
estimatedRotation = webcam->getEstimatedRotation();
} else if (_leadingAvatar) {
_head.getFace().clearFrame();
return;
} else {
_head.setMousePitch(pitchFromTouch);
_head.setPitch(pitchFromTouch);
if (!_leadingAvatar) {
_head.setMousePitch(pitchFromTouch);
_head.setPitch(pitchFromTouch);
}
_head.getFace().clearFrame();
// restore rotation, lean to neutral positions
const float RESTORE_RATE = 0.05f;
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE));
_head.setRoll(glm::mix(_head.getRoll(), 0.0f, RESTORE_RATE));
_head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, RESTORE_RATE));
_head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, RESTORE_RATE));
return;
}
_head.setMousePitch(pitchFromTouch);

View file

@ -8,33 +8,50 @@
#include <QTimer>
#include <SharedUtil.h>
#include "Faceshift.h"
using namespace fs;
using namespace std;
const quint16 FACESHIFT_PORT = 33433;
Faceshift::Faceshift() :
_enabled(false),
_tcpEnabled(false),
_lastMessageReceived(0),
_eyeGazeLeftPitch(0.0f),
_eyeGazeLeftYaw(0.0f),
_eyeGazeRightPitch(0.0f),
_eyeGazeRightYaw(0.0f),
_leftBlink(0.0f),
_rightBlink(0.0f),
_leftBlinkIndex(-1),
_rightBlinkIndex(-1),
_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
_rightBlinkIndex(1),
_leftEyeOpenIndex(8),
_rightEyeOpenIndex(9),
_browHeight(0.0f),
_browUpCenterIndex(-1),
_browUpCenterIndex(16),
_mouthSize(0.0f),
_jawOpenIndex(-1),
_jawOpenIndex(21),
_longTermAverageEyePitch(0.0f),
_longTermAverageEyeYaw(0.0f),
_estimatedEyePitch(0.0f),
_estimatedEyeYaw(0.0f)
{
connect(&_socket, SIGNAL(connected()), SLOT(noteConnected()));
connect(&_socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError)));
connect(&_socket, SIGNAL(readyRead()), SLOT(readFromSocket()));
connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected()));
connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError)));
connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket()));
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
_udpSocket.bind(FACESHIFT_PORT);
}
bool Faceshift::isActive() const {
const uint64_t ACTIVE_TIMEOUT_USECS = 1000000;
return (_tcpSocket.state() == QAbstractSocket::ConnectedState ||
(usecTimestampNow() - _lastMessageReceived) < ACTIVE_TIMEOUT_USECS) && _tracking;
}
void Faceshift::update() {
@ -51,28 +68,28 @@ void Faceshift::update() {
}
void Faceshift::reset() {
if (isActive()) {
if (_tcpSocket.state() == QAbstractSocket::ConnectedState) {
string message;
fsBinaryStream::encode_message(message, fsMsgCalibrateNeutral());
send(message);
}
}
void Faceshift::setEnabled(bool enabled) {
if ((_enabled = enabled)) {
void Faceshift::setTCPEnabled(bool enabled) {
if ((_tcpEnabled = enabled)) {
connectSocket();
} else {
_socket.disconnectFromHost();
_tcpSocket.disconnectFromHost();
}
}
void Faceshift::connectSocket() {
if (_enabled) {
if (_tcpEnabled) {
qDebug("Faceshift: Connecting...\n");
const quint16 FACESHIFT_PORT = 33433;
_socket.connectToHost("localhost", FACESHIFT_PORT);
_tcpSocket.connectToHost("localhost", FACESHIFT_PORT);
_tracking = false;
}
}
@ -86,23 +103,39 @@ void Faceshift::noteConnected() {
}
void Faceshift::noteError(QAbstractSocket::SocketError error) {
qDebug() << "Faceshift: " << _socket.errorString() << "\n";
qDebug() << "Faceshift: " << _tcpSocket.errorString() << "\n";
// reconnect after a delay
if (_enabled) {
if (_tcpEnabled) {
QTimer::singleShot(1000, this, SLOT(connectSocket()));
}
}
void Faceshift::readPendingDatagrams() {
QByteArray buffer;
while (_udpSocket.hasPendingDatagrams()) {
buffer.resize(_udpSocket.pendingDatagramSize());
_udpSocket.readDatagram(buffer.data(), buffer.size());
receive(buffer);
}
}
void Faceshift::readFromSocket() {
QByteArray buffer = _socket.readAll();
receive(_tcpSocket.readAll());
}
void Faceshift::send(const std::string& message) {
_tcpSocket.write(message.data(), message.size());
}
void Faceshift::receive(const QByteArray& buffer) {
_stream.received(buffer.size(), buffer.constData());
fsMsgPtr msg;
for (fsMsgPtr msg; (msg = _stream.get_message()); ) {
switch (msg->id()) {
case fsMsg::MSG_OUT_TRACKING_STATE: {
const fsTrackingData& data = static_cast<fsMsgTrackingState*>(msg.get())->tracking_data();
if (data.m_trackingSuccessful) {
if ((_tracking = data.m_trackingSuccessful)) {
_headRotation = glm::quat(data.m_headRotation.w, -data.m_headRotation.x,
data.m_headRotation.y, -data.m_headRotation.z);
const float TRANSLATION_SCALE = 0.02f;
@ -113,11 +146,12 @@ void Faceshift::readFromSocket() {
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
const float EYE_OPEN_SCALE = 0.5f;
if (_leftBlinkIndex != -1) {
_leftBlink = data.m_coeffs[_leftBlinkIndex];
_leftBlink = data.m_coeffs[_leftBlinkIndex] - data.m_coeffs[_leftEyeOpenIndex] * EYE_OPEN_SCALE;
}
if (_rightBlinkIndex != -1) {
_rightBlink = data.m_coeffs[_rightBlinkIndex];
_rightBlink = data.m_coeffs[_rightBlinkIndex] - data.m_coeffs[_rightEyeOpenIndex] * EYE_OPEN_SCALE;
}
if (_browUpCenterIndex != -1) {
_browHeight = data.m_coeffs[_browUpCenterIndex];
@ -136,7 +170,13 @@ void Faceshift::readFromSocket() {
} else if (names[i] == "EyeBlink_R") {
_rightBlinkIndex = i;
} else if (names[i] == "EyeOpen_L") {
_leftEyeOpenIndex = i;
} else if (names[i] == "EyeOpen_R") {
_rightEyeOpenIndex = i;
} else if (names[i] == "BrowsU_C") {
_browUpCenterIndex = i;
@ -150,8 +190,5 @@ void Faceshift::readFromSocket() {
break;
}
}
}
void Faceshift::send(const std::string& message) {
_socket.write(message.data(), message.size());
_lastMessageReceived = usecTimestampNow();
}

View file

@ -10,6 +10,7 @@
#define __interface__Faceshift__
#include <QTcpSocket>
#include <QUdpSocket>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -24,7 +25,7 @@ public:
Faceshift();
bool isActive() const { return _socket.state() == QAbstractSocket::ConnectedState; }
bool isActive() const;
const glm::quat& getHeadRotation() const { return _headRotation; }
const glm::vec3& getHeadTranslation() const { return _headTranslation; }
@ -50,22 +51,27 @@ public:
public slots:
void setEnabled(bool enabled);
void setTCPEnabled(bool enabled);
private slots:
void connectSocket();
void noteConnected();
void noteError(QAbstractSocket::SocketError error);
void readPendingDatagrams();
void readFromSocket();
private:
void send(const std::string& message);
void receive(const QByteArray& buffer);
QTcpSocket _socket;
QTcpSocket _tcpSocket;
QUdpSocket _udpSocket;
fs::fsBinaryStream _stream;
bool _enabled;
bool _tcpEnabled;
bool _tracking;
uint64_t _lastMessageReceived;
glm::quat _headRotation;
glm::vec3 _headTranslation;
@ -82,6 +88,9 @@ private:
int _leftBlinkIndex;
int _rightBlinkIndex;
int _leftEyeOpenIndex;
int _rightEyeOpenIndex;
float _browHeight;
int _browUpCenterIndex;

View file

@ -13,28 +13,19 @@
const char IPv4_ADDRESS_DESIGNATOR = 4;
const char IPv6_ADDRESS_DESIGNATOR = 6;
Assignment::Assignment(Assignment::Direction direction, Assignment::Type type, const char* pool) :
_direction(direction),
Assignment::Assignment(Assignment::Command command, Assignment::Type type, Assignment::Location location) :
_command(command),
_type(type),
_pool(NULL),
_location(location),
_attachedPublicSocket(NULL),
_attachedLocalSocket(NULL)
{
// set the create time on this assignment
gettimeofday(&_time, NULL);
// copy the pool, if we got one
if (pool) {
int poolLength = strlen(pool);
// create the char array and make it large enough for string and null termination
_pool = new char[poolLength + sizeof(char)];
strcpy(_pool, pool);
}
}
Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
_pool(NULL),
_location(GlobalLocation),
_attachedPublicSocket(NULL),
_attachedLocalSocket(NULL)
{
@ -44,11 +35,11 @@ Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
int numBytesRead = 0;
if (dataBuffer[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
_direction = Assignment::Request;
_command = Assignment::RequestCommand;
} else if (dataBuffer[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
_direction = Assignment::Create;
_command = Assignment::CreateCommand;
} else if (dataBuffer[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT) {
_direction = Assignment::Deploy;
_command = Assignment::DeployCommand;
}
numBytesRead += numBytesForPacketHeader(dataBuffer);
@ -56,15 +47,6 @@ Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
memcpy(&_type, dataBuffer + numBytesRead, sizeof(Assignment::Type));
numBytesRead += sizeof(Assignment::Type);
if (dataBuffer[numBytesRead] != 0) {
int poolLength = strlen((const char*) dataBuffer + numBytesRead);
_pool = new char[poolLength + sizeof(char)];
strcpy(_pool, (char*) dataBuffer + numBytesRead);
numBytesRead += poolLength + sizeof(char);
} else {
numBytesRead += sizeof(char);
}
if (numBytes > numBytesRead) {
sockaddr* newSocket = NULL;
@ -78,7 +60,7 @@ Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
qDebug("Received a socket that cannot be unpacked!\n");
}
if (_direction == Assignment::Create) {
if (_command == Assignment::CreateCommand) {
delete _attachedLocalSocket;
_attachedLocalSocket = newSocket;
} else {
@ -91,7 +73,6 @@ Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
Assignment::~Assignment() {
delete _attachedPublicSocket;
delete _attachedLocalSocket;
delete _pool;
}
void Assignment::setAttachedPublicSocket(const sockaddr* attachedPublicSocket) {
@ -124,16 +105,6 @@ int Assignment::packToBuffer(unsigned char* buffer) {
memcpy(buffer + numPackedBytes, &_type, sizeof(_type));
numPackedBytes += sizeof(_type);
if (_pool) {
int poolLength = strlen(_pool);
strcpy((char*) buffer + numPackedBytes, _pool);
numPackedBytes += poolLength + sizeof(char);
} else {
buffer[numPackedBytes] = '\0';
numPackedBytes += sizeof(char);
}
if (_attachedPublicSocket || _attachedLocalSocket) {
sockaddr* socketToPack = (_attachedPublicSocket) ? _attachedPublicSocket : _attachedLocalSocket;
@ -148,6 +119,6 @@ int Assignment::packToBuffer(unsigned char* buffer) {
}
QDebug operator<<(QDebug debug, const Assignment &assignment) {
debug << "T:" << assignment.getType() << "P:" << assignment.getPool();
debug << "T:" << assignment.getType();
return debug.nospace();
}

View file

@ -18,18 +18,25 @@ class Assignment {
public:
enum Type {
AudioMixer,
AvatarMixer,
All
AudioMixerType,
AvatarMixerType,
AllTypes
};
enum Direction {
Create,
Deploy,
Request
enum Command {
CreateCommand,
DeployCommand,
RequestCommand
};
Assignment(Assignment::Direction direction, Assignment::Type type, const char* pool = NULL);
enum Location {
GlobalLocation,
LocalLocation
};
Assignment(Assignment::Command command,
Assignment::Type type,
Assignment::Location location = Assignment::GlobalLocation);
/// Constructs an Assignment from the data in the buffer
/// \param dataBuffer the source buffer to un-pack the assignment from
@ -38,9 +45,9 @@ public:
~Assignment();
Assignment::Direction getDirection() const { return _direction; }
Assignment::Command getCommand() const { return _command; }
Assignment::Type getType() const { return _type; }
const char* getPool() const { return _pool; }
Assignment::Location getLocation() const { return _location; }
const timeval& getTime() const { return _time; }
const sockaddr* getAttachedPublicSocket() { return _attachedPublicSocket; }
@ -58,9 +65,9 @@ public:
void setCreateTimeToNow() { gettimeofday(&_time, NULL); }
private:
Assignment::Direction _direction; /// the direction of the assignment (Create, Deploy, Request)
Assignment::Command _command; /// the command for this assignment (Create, Deploy, Request)
Assignment::Type _type; /// the type of the assignment, defines what the assignee will do
char* _pool; /// the pool this assignment is for/from
Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs
sockaddr* _attachedPublicSocket; /// pointer to a public socket that relates to assignment, depends on direction
sockaddr* _attachedLocalSocket; /// pointer to a local socket that relates to assignment, depends on direction
timeval _time; /// time the assignment was created (set in constructor)

View file

@ -382,7 +382,7 @@ const sockaddr_in GLOBAL_ASSIGNMENT_SOCKET = socketForHostnameAndHostOrderPort(G
void NodeList::sendAssignment(Assignment& assignment) {
unsigned char assignmentPacket[MAX_PACKET_SIZE];
PACKET_TYPE assignmentPacketType = assignment.getDirection() == Assignment::Create
PACKET_TYPE assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
? PACKET_TYPE_CREATE_ASSIGNMENT
: PACKET_TYPE_REQUEST_ASSIGNMENT;