mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 19:35:48 +02:00
Merge remote-tracking branch 'upstream/master' into rotateFilterChange
This commit is contained in:
commit
baa17395f0
984 changed files with 28289 additions and 14138 deletions
BUILD.mdCMakeLists.txt
android
app
build.gradle
build.gradlesrc/main
AndroidManifest.xml
cpp
java
io/highfidelity/hifiinterface
org/qtproject/qt5/android/bindings
res
assignment-client/src
Agent.cppAgent.hAgentScriptingInterface.cppAgentScriptingInterface.hAssignmentClient.cppAssignmentClientApp.cppAssignmentClientMonitor.cppAssignmentClientMonitor.h
assets
avatars
entities
octree
scripts
cmake
domain-server
resources
src
interface
CMakeLists.txt
resources
avatar
animations
idle.fbxjog_fwd.fbxjog_left.fbxjump_land.fbxjump_takeoff.fbxsettle_to_idle.fbxside_step_left.fbxside_step_left_fast.fbxside_step_left_fast02.fbxside_step_short_left.fbxside_strafe_left.fbxside_strafe_left02.fbxtalk.fbxturn_left.fbxwalk_bwd.fbxwalk_bwd_fast.fbxwalk_fwd.fbxwalk_fwd_fast.fbxwalk_fwd_fast02.fbxwalk_fwd_fast_armout.fbxwalk_left.fbxwalk_left_fast.fbx
avatar-animation.jsonbookmarks
old-avatar-animation.jsonicons/tablet-icons
images
qml
17
BUILD.md
17
BUILD.md
|
@ -1,3 +1,10 @@
|
|||
### OS Specific Build Guides
|
||||
|
||||
* [BUILD_WIN.md](BUILD_WIN.md) - complete instructions for Windows.
|
||||
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
|
||||
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
|
||||
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [cmake](https://cmake.org/download/): 3.9
|
||||
|
@ -27,14 +34,7 @@ These are not placed in your normal build tree when doing an out of source build
|
|||
|
||||
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE\_LOCAL\_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
|
||||
|
||||
### OS Specific Build Guides
|
||||
|
||||
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
|
||||
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
|
||||
* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows.
|
||||
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
|
||||
|
||||
### CMake
|
||||
#### CMake
|
||||
|
||||
Hifi uses CMake to generate build files and project files for your platform.
|
||||
|
||||
|
@ -80,6 +80,7 @@ In the examples below the variable $NAME would be replaced by the name of the de
|
|||
* $NAME_ROOT_DIR - set this variable in your ENV
|
||||
* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name'
|
||||
|
||||
|
||||
### Optional Components
|
||||
|
||||
#### Devices
|
||||
|
|
|
@ -15,6 +15,7 @@ include("cmake/compiler.cmake")
|
|||
|
||||
if (BUILD_SCRIBE_ONLY)
|
||||
add_subdirectory(tools/scribe)
|
||||
add_subdirectory(tools/shader_reflect)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ android {
|
|||
'-DANDROID_STL=c++_shared',
|
||||
'-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake',
|
||||
'-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe' + EXEC_SUFFIX,
|
||||
'-DNATIVE_SHREFLECT=' + HIFI_ANDROID_PRECOMPILED + '/shreflect' + EXEC_SUFFIX,
|
||||
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
|
||||
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
|
||||
'-DRELEASE_TYPE=' + RELEASE_TYPE,
|
||||
|
@ -144,5 +145,7 @@ dependencies {
|
|||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
android:icon="@drawable/ic_launcher"
|
||||
android:launchMode="singleTop"
|
||||
android:roundIcon="@drawable/ic_launcher">
|
||||
<activity android:name="io.highfidelity.hifiinterface.PermissionChecker">
|
||||
<activity android:name="io.highfidelity.hifiinterface.PermissionChecker"
|
||||
android:theme="@style/Theme.AppCompat.Translucent.NoActionBar.Launcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
|
@ -162,7 +162,19 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCrea
|
|||
jvmArgs.version = JNI_VERSION_1_6; // choose your JNI version
|
||||
jvmArgs.name = NULL; // you might want to give the java thread a name
|
||||
jvmArgs.group = NULL; // you might want to assign the java thread to a ThreadGroup
|
||||
jvm->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&myNewEnv), &jvmArgs);
|
||||
|
||||
int attachedHere = 0; // know if detaching at the end is necessary
|
||||
jint res = jvm->GetEnv((void**)&myNewEnv, JNI_VERSION_1_6); // checks if current env needs attaching or it is already attached
|
||||
if (JNI_OK != res) {
|
||||
qDebug() << "[JCRASH] GetEnv env not attached yet, attaching now..";
|
||||
res = jvm->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&myNewEnv), &jvmArgs);
|
||||
if (JNI_OK != res) {
|
||||
qDebug() << "[JCRASH] Failed to AttachCurrentThread, ErrorCode = " << res;
|
||||
return;
|
||||
} else {
|
||||
attachedHere = 1;
|
||||
}
|
||||
}
|
||||
|
||||
QAndroidJniObject string = QAndroidJniObject::fromString(a);
|
||||
jboolean jBackToScene = (jboolean) backToScene;
|
||||
|
@ -175,7 +187,9 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCrea
|
|||
myNewEnv->CallObjectMethod(hashmap, mapClassPut, QAndroidJniObject::fromString("url").object<jstring>(), jArg.object<jstring>());
|
||||
}
|
||||
__interfaceActivity.callMethod<void>("openAndroidActivity", "(Ljava/lang/String;ZLjava/util/HashMap;)V", string.object<jstring>(), jBackToScene, hashmap);
|
||||
jvm->DetachCurrentThread();
|
||||
if (attachedHere) {
|
||||
jvm->DetachCurrentThread();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::hapticFeedbackRequested, [](int duration) {
|
||||
|
@ -195,6 +209,11 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUr
|
|||
DependencyManager::get<AddressManager>()->loadSettings(jniUrl.toString());
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGoToUser(JNIEnv* env, jobject obj, jstring username) {
|
||||
QAndroidJniObject jniUsername("java/lang/String", "(Ljava/lang/String;)V", username);
|
||||
DependencyManager::get<AddressManager>()->goToUser(jniUsername.toString(), false);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
|
||||
}
|
||||
|
||||
|
@ -271,6 +290,18 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
|
|||
Q_ARG(const QString&, username), Q_ARG(const QString&, password));
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return env->NewStringUTF(accountManager->getAccountInfo().getAccessToken().token.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env,
|
||||
jobject instance) {
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.google.vr.ndk.base.GvrApi;*/
|
|||
public class InterfaceActivity extends QtActivity implements WebViewFragment.OnWebViewInteractionListener {
|
||||
|
||||
public static final String DOMAIN_URL = "url";
|
||||
public static final String EXTRA_GOTO_USERNAME = "gotousername";
|
||||
private static final String TAG = "Interface";
|
||||
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
|
||||
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
|
||||
|
@ -59,6 +60,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager);
|
||||
private native void nativeOnDestroy();
|
||||
private native void nativeGotoUrl(String url);
|
||||
private native void nativeGoToUser(String username);
|
||||
private native void nativeBeforeEnterBackground();
|
||||
private native void nativeEnterBackground();
|
||||
private native void nativeEnterForeground();
|
||||
|
@ -280,6 +282,9 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
if (intent.hasExtra(DOMAIN_URL)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGotoUrl(intent.getStringExtra(DOMAIN_URL));
|
||||
} else if (intent.hasExtra(EXTRA_GOTO_USERNAME)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGoToUser(intent.getStringExtra(EXTRA_GOTO_USERNAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.widget.TextView;
|
|||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import io.highfidelity.hifiinterface.fragment.FriendsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.HomeFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.LoginFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
|
||||
|
@ -36,7 +37,8 @@ import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
|
|||
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
|
||||
LoginFragment.OnLoginInteractionListener,
|
||||
HomeFragment.OnHomeInteractionListener {
|
||||
HomeFragment.OnHomeInteractionListener,
|
||||
FriendsFragment.OnHomeInteractionListener {
|
||||
|
||||
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
|
||||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
|
@ -56,6 +58,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private View mLoginPanel;
|
||||
private View mProfilePanel;
|
||||
private TextView mLogoutOption;
|
||||
private MenuItem mPeopleMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
|
||||
|
@ -75,6 +78,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
mDisplayName = mNavigationView.getHeaderView(0).findViewById(R.id.displayName);
|
||||
mProfilePicture = mNavigationView.getHeaderView(0).findViewById(R.id.profilePicture);
|
||||
|
||||
mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
|
||||
setSupportActionBar(toolbar);
|
||||
|
@ -109,40 +114,69 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadLoginFragment();
|
||||
break;
|
||||
case "Home":
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(true);
|
||||
break;
|
||||
case "Privacy Policy":
|
||||
loadPrivacyPolicyFragment();
|
||||
break;
|
||||
case "People":
|
||||
loadPeopleFragment();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown fragment " + fragment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadHomeFragment() {
|
||||
private void loadHomeFragment(boolean addToBackStack) {
|
||||
Fragment fragment = HomeFragment.newInstance();
|
||||
loadFragment(fragment, getString(R.string.home), false);
|
||||
loadFragment(fragment, getString(R.string.home), getString(R.string.tagFragmentHome), addToBackStack);
|
||||
}
|
||||
|
||||
private void loadLoginFragment() {
|
||||
Fragment fragment = LoginFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.login), true);
|
||||
loadFragment(fragment, getString(R.string.login), getString(R.string.tagFragmentLogin), true);
|
||||
}
|
||||
|
||||
private void loadPrivacyPolicyFragment() {
|
||||
Fragment fragment = PolicyFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.privacyPolicy), true);
|
||||
loadFragment(fragment, getString(R.string.privacyPolicy), getString(R.string.tagFragmentPolicy), true);
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment, String title, boolean addToBackStack) {
|
||||
private void loadPeopleFragment() {
|
||||
Fragment fragment = FriendsFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true);
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment, String title, String tag, boolean addToBackStack) {
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
|
||||
// check if it's the same fragment
|
||||
String currentFragmentName = fragmentManager.getBackStackEntryCount() > 0
|
||||
? fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName()
|
||||
: "";
|
||||
if (currentFragmentName.equals(title)) {
|
||||
mDrawerLayout.closeDrawer(mNavigationView);
|
||||
return; // cancel as we are already in that fragment
|
||||
}
|
||||
|
||||
// go back until first transaction
|
||||
int backStackEntryCount = fragmentManager.getBackStackEntryCount();
|
||||
for (int i = 0; i < backStackEntryCount - 1; i++) {
|
||||
fragmentManager.popBackStackImmediate();
|
||||
}
|
||||
|
||||
// this case is when we wanted to go home.. rollback already did that!
|
||||
// But asking for a new Home fragment makes it easier to have an updated list so we let it to continue
|
||||
|
||||
FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.replace(R.id.content_frame, fragment);
|
||||
ft.replace(R.id.content_frame, fragment, tag);
|
||||
|
||||
if (addToBackStack) {
|
||||
ft.addToBackStack(null);
|
||||
ft.addToBackStack(title);
|
||||
}
|
||||
ft.commit();
|
||||
setTitle(title);
|
||||
|
@ -155,11 +189,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
mLoginPanel.setVisibility(View.GONE);
|
||||
mProfilePanel.setVisibility(View.VISIBLE);
|
||||
mLogoutOption.setVisibility(View.VISIBLE);
|
||||
mPeopleMenuItem.setVisible(true);
|
||||
updateProfileHeader();
|
||||
} else {
|
||||
mLoginPanel.setVisibility(View.VISIBLE);
|
||||
mProfilePanel.setVisibility(View.GONE);
|
||||
mLogoutOption.setVisibility(View.GONE);
|
||||
mPeopleMenuItem.setVisible(false);
|
||||
mDisplayName.setText("");
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +236,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case R.id.action_home:
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(false);
|
||||
return true;
|
||||
case R.id.action_people:
|
||||
loadPeopleFragment();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -219,6 +258,19 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public void onLogoutClicked(View view) {
|
||||
nativeLogout();
|
||||
updateLoginMenu();
|
||||
exitLoggedInFragment();
|
||||
|
||||
}
|
||||
|
||||
private void exitLoggedInFragment() {
|
||||
// If we are in a "logged in" fragment (like People), go back to home. This could be expanded to multiple fragments
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
String currentFragmentName = fragmentManager.getBackStackEntryCount() > 0
|
||||
? fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName()
|
||||
: "";
|
||||
if (currentFragmentName.equals(getString(R.string.people))) {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSelectedDomain(String domainUrl) {
|
||||
|
@ -237,9 +289,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void goToUser(String username) {
|
||||
Intent intent = new Intent(this, InterfaceActivity.class);
|
||||
intent.putExtra(InterfaceActivity.EXTRA_GOTO_USERNAME, username);
|
||||
finish();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginCompleted() {
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(false);
|
||||
updateLoginMenu();
|
||||
if (backToScene) {
|
||||
backToScene = false;
|
||||
|
@ -266,6 +326,11 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadPrivacyPolicyFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisitUserSelected(String username) {
|
||||
goToUser(username);
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
@ -284,15 +349,30 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
int index = getFragmentManager().getBackStackEntryCount() - 1;
|
||||
if (index > -1) {
|
||||
// if a fragment needs to internally manage back presses..
|
||||
FragmentManager fm = getFragmentManager();
|
||||
Log.d("[BACK]", "getBackStackEntryCount " + fm.getBackStackEntryCount());
|
||||
Fragment friendsFragment = fm.findFragmentByTag(getString(R.string.tagFragmentPeople));
|
||||
if (friendsFragment != null && friendsFragment instanceof FriendsFragment) {
|
||||
if (((FriendsFragment) friendsFragment).onBackPressed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int index = fm.getBackStackEntryCount() - 1;
|
||||
|
||||
if (index > 0) {
|
||||
super.onBackPressed();
|
||||
index--;
|
||||
if (index > -1) {
|
||||
setTitle(fm.getBackStackEntryAt(index).getName());
|
||||
}
|
||||
if (backToScene) {
|
||||
backToScene = false;
|
||||
goToLastLocation();
|
||||
}
|
||||
} else {
|
||||
finishAffinity();
|
||||
finishAffinity();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.content.Intent;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -135,4 +136,13 @@ public class PermissionChecker extends Activity {
|
|||
launchActivityWithPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
View decorView = getWindow().getDecorView();
|
||||
// Hide the status bar.
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.EndpointUsersProvider;
|
||||
import io.highfidelity.hifiinterface.provider.UsersProvider;
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
|
||||
public class FriendsFragment extends Fragment {
|
||||
|
||||
public native boolean nativeIsLoggedIn();
|
||||
|
||||
public native String nativeGetAccessToken();
|
||||
|
||||
private RecyclerView mUsersView;
|
||||
private View mUserActions;
|
||||
private UserListAdapter mUsersAdapter;
|
||||
private SlidingUpPanelLayout mSlidingUpPanelLayout;
|
||||
private EndpointUsersProvider mUsersProvider;
|
||||
private String mSelectedUsername;
|
||||
|
||||
private OnHomeInteractionListener mListener;
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
public FriendsFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static FriendsFragment newInstance() {
|
||||
FriendsFragment fragment = new FriendsFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_friends, container, false);
|
||||
|
||||
String accessToken = nativeGetAccessToken();
|
||||
mUsersProvider = new EndpointUsersProvider(accessToken);
|
||||
|
||||
Log.d("[USERS]", "token : [" + accessToken + "]");
|
||||
|
||||
mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||
|
||||
mUsersView = rootView.findViewById(R.id.rvUsers);
|
||||
int numberOfColumns = 1;
|
||||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
mUsersView.setLayoutManager(gridLayoutMgr);
|
||||
|
||||
mUsersAdapter = new UserListAdapter(getContext(), mUsersProvider);
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
|
||||
mUserActions = rootView.findViewById(R.id.userActionsLayout);
|
||||
|
||||
mSlidingUpPanelLayout = rootView.findViewById(R.id.sliding_layout);
|
||||
mSlidingUpPanelLayout.setPanelHeight(0);
|
||||
|
||||
rootView.findViewById(R.id.userActionDelete).setOnClickListener(view -> onRemoveConnectionClick());
|
||||
|
||||
rootView.findViewById(R.id.userActionVisit).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mListener != null && mSelectedUsername != null) {
|
||||
mListener.onVisitUserSelected(mSelectedUsername);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mUsersAdapter.setClickListener(new UserListAdapter.ItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View view, int position, UserListAdapter.User user) {
|
||||
// 1. 'select' user
|
||||
mSelectedUsername = user.name;
|
||||
// ..
|
||||
// 2. adapt options
|
||||
// ..
|
||||
rootView.findViewById(R.id.userActionVisit).setVisibility(user.online ? View.VISIBLE : View.GONE);
|
||||
// 3. show
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
|
||||
}
|
||||
});
|
||||
|
||||
mUsersAdapter.setListener(new UserListAdapter.AdapterListener() {
|
||||
@Override
|
||||
public void onEmptyAdapter() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonEmptyAdapter() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e, String message) {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
mUsersView.setAdapter(mUsersAdapter);
|
||||
|
||||
mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
}
|
||||
});
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> mUsersAdapter.loadUsers());
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void onRemoveConnectionClick() {
|
||||
if (mSelectedUsername == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage("Remove '" + mSelectedUsername + "' from People?");
|
||||
builder.setPositiveButton("Remove", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
mUsersProvider.removeConnection(mSelectedUsername, new UsersProvider.UserActionCallback() {
|
||||
@Override
|
||||
public void requestOk() {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
mUsersAdapter.loadUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestError(Exception e, String message) {
|
||||
// CLD: Show error message?
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
// Cancelled, nothing to do
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the back pressed event and returns true if it was managed by this Fragment
|
||||
* @return
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
if (mSlidingUpPanelLayout.getPanelState().equals(SlidingUpPanelLayout.PanelState.EXPANDED)) {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnHomeInteractionListener) {
|
||||
mListener = (OnHomeInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnHomeInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnHomeInteractionListener {
|
||||
void onVisitUserSelected(String username);
|
||||
}
|
||||
}
|
|
@ -66,6 +66,7 @@ public class HomeFragment extends Fragment {
|
|||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
mDomainsView.setLayoutManager(gridLayoutMgr);
|
||||
mDomainAdapter = new DomainAdapter(getContext(), HifiUtils.getInstance().protocolVersionSignature(), nativeGetLastLocation());
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
mDomainAdapter.setClickListener((view, position, domain) -> {
|
||||
new Handler(getActivity().getMainLooper()).postDelayed(() -> {
|
||||
if (mListener != null) {
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public class EndpointUsersProvider implements UsersProvider {
|
||||
|
||||
public static final String BASE_URL = "https://metaverse.highfidelity.com/";
|
||||
private final Retrofit mRetrofit;
|
||||
private final EndpointUsersProviderService mEndpointUsersProviderService;
|
||||
|
||||
public EndpointUsersProvider(String accessToken) {
|
||||
mRetrofit = createAuthorizedRetrofit(accessToken);
|
||||
mEndpointUsersProviderService = mRetrofit.create(EndpointUsersProviderService.class);
|
||||
}
|
||||
|
||||
private Retrofit createAuthorizedRetrofit(String accessToken) {
|
||||
Retrofit mRetrofit;
|
||||
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
|
||||
httpClient.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request original = chain.request();
|
||||
|
||||
Request request = original.newBuilder()
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.method(original.method(), original.body())
|
||||
.build();
|
||||
|
||||
return chain.proceed(request);
|
||||
}
|
||||
});
|
||||
|
||||
OkHttpClient client = httpClient.build();
|
||||
|
||||
mRetrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(client)
|
||||
.build();
|
||||
return mRetrofit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieve(UsersCallback usersCallback) {
|
||||
Call<UsersResponse> friendsCall = mEndpointUsersProviderService.getUsers(
|
||||
CONNECTION_FILTER_CONNECTIONS,
|
||||
400,
|
||||
null);
|
||||
friendsCall.enqueue(new Callback<UsersResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<UsersResponse> call, retrofit2.Response<UsersResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
usersCallback.retrieveError(new Exception("Error calling Users API"), "Error calling Users API");
|
||||
return;
|
||||
}
|
||||
UsersResponse usersResponse = response.body();
|
||||
List<UserListAdapter.User> adapterUsers = new ArrayList<>(usersResponse.total_entries);
|
||||
for (User user : usersResponse.data.users) {
|
||||
UserListAdapter.User adapterUser = new UserListAdapter.User();
|
||||
adapterUser.connection = user.connection;
|
||||
adapterUser.imageUrl = user.images.thumbnail;
|
||||
adapterUser.name = user.username;
|
||||
adapterUser.online = user.online;
|
||||
adapterUser.locationName = (user.location != null ?
|
||||
(user.location.root != null ? user.location.root.name :
|
||||
(user.location.domain != null ? user.location.domain.name : ""))
|
||||
: "");
|
||||
adapterUsers.add(adapterUser);
|
||||
}
|
||||
usersCallback.retrieveOk(adapterUsers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UsersResponse> call, Throwable t) {
|
||||
usersCallback.retrieveError(new Exception(t), "Error calling Users API");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class UserActionRetrofitCallback implements Callback<UsersResponse> {
|
||||
|
||||
UserActionCallback callback;
|
||||
|
||||
public UserActionRetrofitCallback(UserActionCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<UsersResponse> call, retrofit2.Response<UsersResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
callback.requestError(new Exception("Error with "
|
||||
+ call.request().url().toString() + " "
|
||||
+ call.request().method() + " call " + response.message()),
|
||||
response.message());
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.body() == null || !"success".equals(response.body().status)) {
|
||||
callback.requestError(new Exception("Error with "
|
||||
+ call.request().url().toString() + " "
|
||||
+ call.request().method() + " call " + response.message()),
|
||||
response.message());
|
||||
return;
|
||||
}
|
||||
callback.requestOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UsersResponse> call, Throwable t) {
|
||||
callback.requestError(new Exception(t), t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFriend(String friendUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> friendCall = mEndpointUsersProviderService.addFriend(new BodyAddFriend(friendUserName));
|
||||
friendCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFriend(String friendUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> friendCall = mEndpointUsersProviderService.removeFriend(friendUserName);
|
||||
friendCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConnection(String connectionUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> connectionCall = mEndpointUsersProviderService.removeConnection(connectionUserName);
|
||||
connectionCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
public interface EndpointUsersProviderService {
|
||||
@GET("api/v1/users")
|
||||
Call<UsersResponse> getUsers(@Query("filter") String filter,
|
||||
@Query("per_page") int perPage,
|
||||
@Query("online") Boolean online);
|
||||
|
||||
@DELETE("api/v1/user/connections/{connectionUserName}")
|
||||
Call<UsersResponse> removeConnection(@Path("connectionUserName") String connectionUserName);
|
||||
|
||||
@DELETE("api/v1/user/friends/{friendUserName}")
|
||||
Call<UsersResponse> removeFriend(@Path("friendUserName") String friendUserName);
|
||||
|
||||
@POST("api/v1/user/friends")
|
||||
Call<UsersResponse> addFriend(@Body BodyAddFriend friendUserName);
|
||||
|
||||
/* response
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class BodyAddFriend {
|
||||
String username;
|
||||
public BodyAddFriend(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
|
||||
class UsersResponse {
|
||||
public UsersResponse() {}
|
||||
String status;
|
||||
int current_page;
|
||||
int total_pages;
|
||||
int per_page;
|
||||
int total_entries;
|
||||
Data data;
|
||||
}
|
||||
|
||||
class Data {
|
||||
public Data() {}
|
||||
List<User> users;
|
||||
}
|
||||
|
||||
class User {
|
||||
public User() {}
|
||||
String username;
|
||||
boolean online;
|
||||
String connection;
|
||||
Images images;
|
||||
LocationData location;
|
||||
}
|
||||
|
||||
class Images {
|
||||
public Images() {}
|
||||
String hero;
|
||||
String thumbnail;
|
||||
String tiny;
|
||||
}
|
||||
|
||||
class LocationData {
|
||||
public LocationData() {}
|
||||
NameContainer root;
|
||||
NameContainer domain;
|
||||
}
|
||||
class NameContainer {
|
||||
String name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public interface UsersProvider {
|
||||
|
||||
public static String CONNECTION_TYPE_FRIEND = "friend";
|
||||
public static String CONNECTION_FILTER_CONNECTIONS = "connections";
|
||||
|
||||
void retrieve(UsersProvider.UsersCallback usersCallback);
|
||||
|
||||
interface UsersCallback {
|
||||
void retrieveOk(List<UserListAdapter.User> users);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
||||
|
||||
void addFriend(String friendUserName, UserActionCallback callback);
|
||||
|
||||
void removeFriend(String friendUserName, UserActionCallback callback);
|
||||
|
||||
void removeConnection(String connectionUserName, UserActionCallback callback);
|
||||
|
||||
interface UserActionCallback {
|
||||
void requestOk();
|
||||
void requestError(Exception e, String message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
package io.highfidelity.hifiinterface.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.UsersProvider;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHolder> {
|
||||
|
||||
private UsersProvider mProvider;
|
||||
private LayoutInflater mInflater;
|
||||
private Context mContext;
|
||||
private List<User> mUsers = new ArrayList<>();
|
||||
private ItemClickListener mClickListener;
|
||||
private AdapterListener mAdapterListener;
|
||||
|
||||
public UserListAdapter(Context c, UsersProvider usersProvider) {
|
||||
mContext = c;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mProvider = usersProvider;
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
public void setListener(AdapterListener adapterListener) {
|
||||
mAdapterListener = adapterListener;
|
||||
}
|
||||
|
||||
public void loadUsers() {
|
||||
mProvider.retrieve(new UsersProvider.UsersCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<User> users) {
|
||||
mUsers = new ArrayList<>(users);
|
||||
notifyDataSetChanged();
|
||||
if (mAdapterListener != null) {
|
||||
if (mUsers.isEmpty()) {
|
||||
mAdapterListener.onEmptyAdapter();
|
||||
} else {
|
||||
mAdapterListener.onNonEmptyAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveError(Exception e, String message) {
|
||||
Log.e("[USERS]", message, e);
|
||||
if (mAdapterListener != null) {
|
||||
mAdapterListener.onError(e, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = mInflater.inflate(R.layout.user_item, parent, false);
|
||||
return new UserListAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(UserListAdapter.ViewHolder holder, int position) {
|
||||
User aUser = mUsers.get(position);
|
||||
holder.mUsername.setText(aUser.name);
|
||||
|
||||
holder.mOnlineInfo.setVisibility(aUser.online? View.VISIBLE : View.GONE);
|
||||
holder.mLocation.setText("- " + aUser.locationName); // Bring info from the API and use it here
|
||||
|
||||
holder.mFriendStar.onBindSet(aUser.name, aUser.connection.equals(UsersProvider.CONNECTION_TYPE_FRIEND));
|
||||
Uri uri = Uri.parse(aUser.imageUrl);
|
||||
Picasso.get().load(uri).into(holder.mImage, new RoundProfilePictureCallback(holder.mImage));
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
private ImageView mProfilePicture;
|
||||
public RoundProfilePictureCallback(ImageView imageView) {
|
||||
mProfilePicture = imageView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Bitmap imageBitmap = ((BitmapDrawable) mProfilePicture.getDrawable()).getBitmap();
|
||||
RoundedBitmapDrawable imageDrawable = RoundedBitmapDrawableFactory.create(mProfilePicture.getContext().getResources(), imageBitmap);
|
||||
imageDrawable.setCircular(true);
|
||||
imageDrawable.setCornerRadius(Math.max(imageBitmap.getWidth(), imageBitmap.getHeight()) / 2.0f);
|
||||
mProfilePicture.setImageDrawable(imageDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
mProfilePicture.setImageResource(R.drawable.default_profile_avatar);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mUsers.size();
|
||||
}
|
||||
|
||||
public class ToggleWrapper {
|
||||
|
||||
private ViewGroup mFrame;
|
||||
private ImageView mImage;
|
||||
private boolean mChecked = false;
|
||||
private String mUsername;
|
||||
private boolean waitingChangeConfirm = false;
|
||||
|
||||
public ToggleWrapper(ViewGroup toggleFrame) {
|
||||
mFrame = toggleFrame;
|
||||
mImage = toggleFrame.findViewById(R.id.userFavImage);
|
||||
mFrame.setOnClickListener(view -> toggle());
|
||||
}
|
||||
|
||||
private void refreshUI() {
|
||||
mImage.setColorFilter(ContextCompat.getColor(mImage.getContext(),
|
||||
mChecked ? R.color.starSelectedTint : R.color.starUnselectedTint));
|
||||
}
|
||||
|
||||
class RollbackUICallback implements UsersProvider.UserActionCallback {
|
||||
|
||||
boolean previousStatus;
|
||||
|
||||
RollbackUICallback(boolean previousStatus) {
|
||||
this.previousStatus = previousStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestOk() {
|
||||
if (!waitingChangeConfirm) {
|
||||
return;
|
||||
}
|
||||
mFrame.setClickable(true);
|
||||
// nothing to do, new status was set
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestError(Exception e, String message) {
|
||||
if (!waitingChangeConfirm) {
|
||||
return;
|
||||
}
|
||||
// new status was not set, rolling back
|
||||
mChecked = previousStatus;
|
||||
mFrame.setClickable(true);
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void toggle() {
|
||||
// TODO API CALL TO CHANGE
|
||||
final boolean previousStatus = mChecked;
|
||||
mChecked = !mChecked;
|
||||
mFrame.setClickable(false);
|
||||
refreshUI();
|
||||
waitingChangeConfirm = true;
|
||||
if (mChecked) {
|
||||
mProvider.addFriend(mUsername, new RollbackUICallback(previousStatus));
|
||||
} else {
|
||||
mProvider.removeFriend(mUsername, new RollbackUICallback(previousStatus));
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBindSet(String username, boolean checked) {
|
||||
mChecked = checked;
|
||||
mUsername = username;
|
||||
waitingChangeConfirm = false;
|
||||
mFrame.setClickable(true);
|
||||
refreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
TextView mUsername;
|
||||
TextView mOnline;
|
||||
View mOnlineInfo;
|
||||
TextView mLocation;
|
||||
ImageView mImage;
|
||||
ToggleWrapper mFriendStar;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mUsername = itemView.findViewById(R.id.userName);
|
||||
mOnline = itemView.findViewById(R.id.userOnline);
|
||||
mImage = itemView.findViewById(R.id.userImage);
|
||||
mOnlineInfo = itemView.findViewById(R.id.userOnlineInfo);
|
||||
mLocation = itemView.findViewById(R.id.userLocation);
|
||||
mFriendStar = new ToggleWrapper(itemView.findViewById(R.id.userFav));
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int position = getAdapterPosition();
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onItemClick(view, position, mUsers.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// allows clicks events to be caught
|
||||
public void setClickListener(ItemClickListener itemClickListener) {
|
||||
this.mClickListener = itemClickListener;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position, User user);
|
||||
}
|
||||
|
||||
public static class User {
|
||||
public String name;
|
||||
public String imageUrl;
|
||||
public String connection;
|
||||
public boolean online;
|
||||
|
||||
public String locationName;
|
||||
|
||||
public User() {}
|
||||
}
|
||||
|
||||
public interface AdapterListener {
|
||||
void onEmptyAdapter();
|
||||
void onNonEmptyAdapter();
|
||||
void onError(Exception e, String message);
|
||||
}
|
||||
}
|
|
@ -60,6 +60,8 @@ import android.view.View;
|
|||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class QtActivity extends Activity {
|
||||
public String APPLICATION_PARAMETERS = null;
|
||||
|
@ -103,7 +105,7 @@ public class QtActivity extends Activity {
|
|||
}
|
||||
|
||||
public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
return super_dispatchPopulateAccessibilityEvent(event);
|
||||
return super.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -362,7 +364,25 @@ public class QtActivity extends Activity {
|
|||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
QtApplication.invokeDelegate();
|
||||
/*
|
||||
cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
|
||||
After Qt upgrade to 5.11 we had a black screen crash after closing the application with
|
||||
the hardware button "Back" and trying to start the app again. It could only be fixed after
|
||||
totally closing the app swiping it in the list of running apps.
|
||||
This problem did not happen with the previous Qt version.
|
||||
After analysing changes we came up with this case and change:
|
||||
https://codereview.qt-project.org/#/c/218882/
|
||||
In summary they've moved libs loading to the same thread as main() and as a matter of correctness
|
||||
in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
|
||||
That exit call is the main reason of this problem.
|
||||
|
||||
In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
|
||||
entire onDestroy method including that thread exit line for other three lines that purposely
|
||||
terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
|
||||
*/
|
||||
QtNative.terminateQt();
|
||||
QtNative.setActivity(null, null);
|
||||
System.exit(0);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
4
android/app/src/main/res/drawable/ic_star.xml
Normal file
4
android/app/src/main/res/drawable/ic_star.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="31dp" android:viewportHeight="25.0"
|
||||
android:viewportWidth="27.0" android:width="31dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FBD92A" android:pathData="M12.549,0.927C12.848,0.006 14.152,0.006 14.451,0.927L16.756,8.019C16.889,8.431 17.273,8.71 17.706,8.71H25.164C26.132,8.71 26.535,9.95 25.751,10.519L19.719,14.903C19.368,15.157 19.221,15.608 19.355,16.021L21.66,23.113C21.959,24.034 20.904,24.8 20.121,24.231L14.088,19.847C13.737,19.593 13.263,19.593 12.912,19.847L6.879,24.231C6.096,24.8 5.041,24.034 5.34,23.113L7.645,16.021C7.779,15.608 7.632,15.157 7.282,14.903L1.249,10.519C0.465,9.95 0.868,8.71 1.836,8.71H9.293C9.727,8.71 10.111,8.431 10.245,8.019L12.549,0.927Z"/>
|
||||
</vector>
|
31
android/app/src/main/res/drawable/ic_teleporticon.xml
Normal file
31
android/app/src/main/res/drawable/ic_teleporticon.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h22.8v18.7h-22.8z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h16.3v18.7h-16.3z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h16.3v18.7h-16.3z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.8,9.9h9.6v7.8h-9.6z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="M11.9,16.9c-0.2,-0.9 -0.3,-2.3 -0.4,-3.4c-0.1,-0.7 -0.1,-1.3 -0.2,-1.7c0,-0.1 -0.1,-0.3 0.3,-0.4c0.1,0 0.1,0 0.2,-0.1l4.4,-1.7c0.3,-0.1 0.5,-0.4 0.6,-0.7c0.1,-0.3 0.1,-0.7 -0.2,-0.9L16.6,8c-0.2,-0.2 -0.5,-0.3 -0.8,-0.3c-0.1,0 -4.8,0.7 -6.8,0.7c-0.1,0 -0.1,0 -0.1,0c-2,0 -6.9,-0.8 -7,-0.8c-0.4,-0.1 -0.8,0.1 -1,0.4L0.7,8.3C0.6,8.5 0.6,8.8 0.6,9.1c0.1,0.3 0.3,0.5 0.5,0.6C2,10 5,11.2 5.9,11.3c0.2,0 0.4,0.1 0.5,0.6c0.1,0.6 -0.2,3.6 -0.6,5c-0.4,1.4 -1,3.2 -1,3.2c-0.2,0.5 0.1,1 0.6,1.2l0.6,0.2c0.2,0.1 0.5,0.1 0.7,-0.1c0.2,-0.1 0.4,-0.3 0.5,-0.6l1.7,-5l1.6,5.1c0.1,0.3 0.3,0.5 0.5,0.6c0.1,0.1 0.3,0.1 0.4,0.1c0.1,0 0.2,0 0.3,-0.1l0.5,-0.2c0.4,-0.2 0.7,-0.6 0.6,-1.1C12.8,20.3 12.3,18.5 11.9,16.9z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M8.9,7.5c1.3,0 2.3,-1 2.3,-2.3S10.2,3 8.9,3S6.6,4 6.6,5.3S7.7,7.5 8.9,7.5z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M23,13.4L22.6,13c0,0 0,0 0,0l-2.9,-2.8c-0.2,-0.2 -0.5,-0.2 -0.7,0l-0.7,0.7c-0.2,0.2 -0.2,0.5 0,0.7l1.2,1.2h-5.2c-0.3,0 -0.5,0.2 -0.5,0.5v0.9c0,0.3 0.2,0.5 0.5,0.5h5.1l-1.2,1.1c-0.2,0.2 -0.2,0.5 0,0.7l0.7,0.7c0.2,0.2 0.5,0.2 0.7,0l3.3,-3.2C23.2,13.9 23.2,13.6 23,13.4z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
11
android/app/src/main/res/drawable/launch_screen.xml
Normal file
11
android/app/src/main/res/drawable/launch_screen.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
|
||||
<item android:drawable="@android:color/black"/>
|
||||
<item android:drawable="@drawable/hifi_logo_splash"
|
||||
android:gravity="center_horizontal"
|
||||
android:top="225dp"
|
||||
android:height="242dp"
|
||||
android:width="242dp">
|
||||
</item>
|
||||
</layer-list>
|
|
@ -7,9 +7,17 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black">
|
||||
<ImageView
|
||||
android:id="@+id/hifi_logo"
|
||||
android:layout_width="242dp"
|
||||
android:layout_height="242dp"
|
||||
android:src="@drawable/hifi_logo_splash"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="225dp"/>
|
||||
<ProgressBar
|
||||
style="@style/Widget.AppCompat.ProgressBar"
|
||||
android:theme="@style/HifiCircularProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/hifi_logo"
|
||||
android:layout_centerHorizontal="true"/>
|
||||
</RelativeLayout>
|
||||
|
|
88
android/app/src/main/res/layout/fragment_friends.xml
Normal file
88
android/app/src/main/res/layout/fragment_friends.xml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.sothree.slidinguppanel.SlidingUpPanelLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/sliding_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
app:umanoFadeColor="@color/slidingUpPanelFadeColor"
|
||||
app:umanoShadowHeight="4dp"
|
||||
android:background="@color/backgroundLight">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvUsers"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/userActionsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="270dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/backgroundDark">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/userActionVisit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView android:id="@+id/userActionVisitIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_teleporticon"
|
||||
android:tint="@color/white_opaque" />
|
||||
<TextView android:id="@+id/userActionVisitText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Visit In-World"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/white_opaque"
|
||||
app:layout_constraintStart_toEndOf="@id/userActionVisitIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="32dp" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/userActionDelete"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView android:id="@+id/userActionDeleteIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_delete_black_24dp"
|
||||
android:tint="@color/white_opaque" />
|
||||
<TextView android:id="@+id/userActionDeleteText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Remove from People"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/white_opaque"
|
||||
app:layout_constraintStart_toEndOf="@id/userActionDeleteIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="32dp" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
|
69
android/app/src/main/res/layout/user_item.xml
Normal file
69
android/app/src/main/res/layout/user_item.xml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/userImage"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
app:layout_constraintStart_toEndOf="@id/userImage"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/userName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/menuOption"/>
|
||||
<LinearLayout android:id="@+id/userOnlineInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/userOnline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/online"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/hifiAquamarine" />
|
||||
<TextView
|
||||
android:id="@+id/userLocation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:fontFamily="@font/raleway_italic"
|
||||
android:textColor="@color/menuOption"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<RelativeLayout android:id="@+id/userFav"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginEnd="5.5dp">
|
||||
<ImageView android:id="@+id/userFavImage"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:src="@drawable/ic_star"
|
||||
android:tint="@color/starUnselectedTint"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginEnd="0dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -5,4 +5,8 @@
|
|||
android:id="@+id/action_home"
|
||||
android:title="@string/home"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_people"
|
||||
android:title="@string/people"
|
||||
/>
|
||||
</menu>
|
||||
|
|
|
@ -17,4 +17,9 @@
|
|||
<color name="colorLoginError">#FF7171</color>
|
||||
<color name="black_060">#99000000</color>
|
||||
<color name="statusbar_color">#292929</color>
|
||||
<color name="hifiLogoColor">#23B2E7</color>
|
||||
<color name="hifiAquamarine">#62D5C6</color>
|
||||
<color name="starSelectedTint">#FBD92A</color>
|
||||
<color name="starUnselectedTint">#8A8A8A</color>
|
||||
<color name="slidingUpPanelFadeColor">#40000000</color>
|
||||
</resources>
|
||||
|
|
|
@ -37,4 +37,6 @@
|
|||
<dimen name="header_hifi_height">101dp</dimen>
|
||||
<dimen name="header_hifi_width">425dp</dimen>
|
||||
|
||||
<dimen name="list_vertical_padding">8dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">Interface</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="people">People</string>
|
||||
<string name="web_view_action_open_in_browser" translatable="false">Open in browser</string>
|
||||
<string name="web_view_action_share" translatable="false">Share link</string>
|
||||
<string name="web_view_action_share_subject" translatable="false">Shared a link</string>
|
||||
|
@ -21,5 +22,11 @@
|
|||
<string name="search_no_results">No places exist with that name</string>
|
||||
<string name="privacyPolicy">Privacy Policy</string>
|
||||
<string name="your_last_location">Your Last Location</string>
|
||||
<string name="online">Online</string>
|
||||
|
||||
<!-- tags -->
|
||||
<string name="tagFragmentHome">tagFragmentHome</string>
|
||||
<string name="tagFragmentLogin">tagFragmentLogin</string>
|
||||
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
|
||||
<string name="tagFragmentPeople">tagFragmentPeople</string>
|
||||
</resources>
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
<style name="Theme.AppCompat.Translucent.NoActionBar.Launcher" parent="Theme.AppCompat.Translucent.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/launch_screen</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
|
@ -62,4 +65,7 @@
|
|||
<item name="android:background">@color/white_opaque</item>
|
||||
</style>
|
||||
|
||||
<style name="HifiCircularProgress" parent="Theme.AppCompat.Light">
|
||||
<item name="colorAccent">@color/hifiLogoColor</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -28,6 +28,7 @@ allprojects {
|
|||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,18 +165,29 @@ def packages = [
|
|||
|
||||
|
||||
def scribeLocalFile='scribe' + EXEC_SUFFIX
|
||||
|
||||
def scribeFile='scribe_linux_x86_64'
|
||||
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
|
||||
def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G'
|
||||
|
||||
def shreflectLocalFile='shreflect' + EXEC_SUFFIX
|
||||
def shreflectFile='shreflect_linux_x86_64'
|
||||
def shreflectChecksum='d6094a8580066c0b6f4e80b5adfb1d98'
|
||||
def shreflectVersion='jnrpudh6fptIg6T2.Z6fgKP2ultAdKmE'
|
||||
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
scribeFile = 'scribe_osx_x86_64'
|
||||
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
|
||||
scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC'
|
||||
shreflectFile='shreflect_osx_x86_64'
|
||||
shreflectChecksum='d613ef0703c21371fee93fd2e54b964f'
|
||||
shreflectVersion='.rYNzjSFq6WtWDnE5KIKRIAGyJtr__ad'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
scribeFile = 'scribe_win32_x86_64.exe'
|
||||
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
|
||||
scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7'
|
||||
shreflectFile='shreflect_win32_x86_64.exe'
|
||||
shreflectChecksum='6f4a77b8cceb3f1bbc655132c3665060'
|
||||
shreflectVersion='iIyCyza1nelkbI7ihybF59bBlwrfAC3D'
|
||||
}
|
||||
|
||||
def options = [
|
||||
|
@ -450,11 +462,27 @@ task fixScribePermissions(type: Exec, dependsOn: verifyScribe) {
|
|||
commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + scribeLocalFile
|
||||
}
|
||||
|
||||
task setupScribe(dependsOn: verifyScribe) { }
|
||||
task downloadShreflect(type: Download) {
|
||||
src baseUrl + shreflectFile + '?versionId=' + shreflectVersion
|
||||
dest new File(baseFolder, shreflectLocalFile)
|
||||
onlyIfNewer true
|
||||
}
|
||||
|
||||
task verifyShreflect(type: Verify, dependsOn: downloadShreflect) {
|
||||
src new File(baseFolder, shreflectLocalFile);
|
||||
checksum shreflectChecksum
|
||||
}
|
||||
|
||||
task fixShreflectPermissions(type: Exec, dependsOn: verifyShreflect) {
|
||||
commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + shreflectLocalFile
|
||||
}
|
||||
|
||||
task setupScribe(dependsOn: [verifyScribe, verifyShreflect]) { }
|
||||
|
||||
// On Windows, we don't need to set the executable bit, but on OSX and Unix we do
|
||||
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
setupScribe.dependsOn fixScribePermissions
|
||||
setupScribe.dependsOn fixShreflectPermissions
|
||||
}
|
||||
|
||||
task extractGvrBinaries(dependsOn: extractDependencies) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QThread>
|
||||
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <AssetClient.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include <ResourceCache.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <SoundCache.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
|
@ -50,6 +52,7 @@
|
|||
#include "entities/AssignmentParentFinder.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "AbstractAudioInterface.h"
|
||||
#include "AgentScriptingInterface.h"
|
||||
|
||||
|
||||
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
|
||||
|
@ -70,6 +73,7 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<AudioScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
|
@ -78,8 +82,6 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
DependencyManager::set<recording::ClipCache>();
|
||||
|
||||
DependencyManager::set<ScriptCache>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
|
||||
|
||||
DependencyManager::set<RecordingScriptingInterface>();
|
||||
DependencyManager::set<UsersScriptingInterface>();
|
||||
|
||||
|
@ -158,6 +160,8 @@ void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
|||
static const QString AGENT_LOGGING_NAME = "agent";
|
||||
|
||||
void Agent::run() {
|
||||
// Create ScriptEngines on threaded-assignment thread then move to main thread.
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT)->moveToThread(qApp->thread());
|
||||
|
||||
// make sure we request our script once the agent connects to the domain
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -344,8 +348,6 @@ void Agent::scriptRequestFinished() {
|
|||
void Agent::executeScript() {
|
||||
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
|
||||
|
||||
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
|
||||
|
||||
// setup an Avatar for the script to use
|
||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
|
||||
|
@ -366,7 +368,6 @@ void Agent::executeScript() {
|
|||
// give scripts access to the Users object
|
||||
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
|
||||
if (player->isPlaying()) {
|
||||
|
@ -452,10 +453,10 @@ void Agent::executeScript() {
|
|||
packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket");
|
||||
|
||||
// register ourselves to the script engine
|
||||
_scriptEngine->registerGlobalObject("Agent", this);
|
||||
_scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this));
|
||||
|
||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
||||
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
@ -496,7 +497,6 @@ void Agent::executeScript() {
|
|||
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
|
||||
|
||||
DependencyManager::destroy<RecordingScriptingInterface>();
|
||||
|
||||
setFinished(true);
|
||||
}
|
||||
|
||||
|
@ -515,7 +515,7 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
[](const SharedNodePointer& node)->bool {
|
||||
return (node->getType() == NodeType::AudioMixer) && node->getActiveSocket();
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
|
@ -825,10 +825,6 @@ void Agent::processAgentAvatarAudio() {
|
|||
void Agent::aboutToFinish() {
|
||||
setIsAvatar(false);// will stop timers for sending identity packets
|
||||
|
||||
if (_scriptEngine) {
|
||||
_scriptEngine->stop();
|
||||
}
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
|
@ -841,16 +837,16 @@ void Agent::aboutToFinish() {
|
|||
|
||||
// destroy all other created dependencies
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<ScriptEngines>();
|
||||
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
||||
DependencyManager::destroy<recording::Deck>();
|
||||
DependencyManager::destroy<recording::Recorder>();
|
||||
DependencyManager::destroy<recording::ClipCache>();
|
||||
|
||||
DependencyManager::destroy<ScriptEngine>();
|
||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
|
||||
|
||||
// cleanup codec & encoder
|
||||
|
@ -859,3 +855,11 @@ void Agent::aboutToFinish() {
|
|||
_encoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::stop() {
|
||||
if (_scriptEngine) {
|
||||
_scriptEngine->stop();
|
||||
} else {
|
||||
setFinished(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,19 +33,6 @@
|
|||
#include "entities/EntityTreeHeadlessViewer.h"
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Agent
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} isAvatar
|
||||
* @property {boolean} isPlayingAvatarSound <em>Read-only.</em>
|
||||
* @property {boolean} isListeningToAudioStream
|
||||
* @property {boolean} isNoiseGateEnabled
|
||||
* @property {number} lastReceivedAudioLoudness <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
class Agent : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -73,30 +60,15 @@ public:
|
|||
virtual void aboutToFinish() override;
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* @function Agent.run
|
||||
* @deprecated This function is being removed from the API.
|
||||
*/
|
||||
void run() override;
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.playAvatarSound
|
||||
* @param {object} avatarSound
|
||||
*/
|
||||
void playAvatarSound(SharedSoundPointer avatarSound);
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.setIsAvatar
|
||||
* @param {boolean} isAvatar
|
||||
*/
|
||||
void setIsAvatar(bool isAvatar);
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.isAvatar
|
||||
* @returns {boolean}
|
||||
*/
|
||||
void setIsAvatar(bool isAvatar);
|
||||
bool isAvatar() const { return _isAvatar; }
|
||||
|
||||
Q_INVOKABLE virtual void stop() override;
|
||||
|
||||
private slots:
|
||||
void requestScript();
|
||||
void scriptRequestFinished();
|
||||
|
|
17
assignment-client/src/AgentScriptingInterface.cpp
Normal file
17
assignment-client/src/AgentScriptingInterface.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// AgentScriptingInterface.cpp
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Thijs Wenker on 7/23/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AgentScriptingInterface.h"
|
||||
|
||||
AgentScriptingInterface::AgentScriptingInterface(Agent* agent) :
|
||||
QObject(agent),
|
||||
_agent(agent)
|
||||
{ }
|
80
assignment-client/src/AgentScriptingInterface.h
Normal file
80
assignment-client/src/AgentScriptingInterface.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// AgentScriptingInterface.h
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Thijs Wenker on 7/23/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_AgentScriptingInterface_h
|
||||
#define hifi_AgentScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Agent
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} isAvatar
|
||||
* @property {boolean} isPlayingAvatarSound <em>Read-only.</em>
|
||||
* @property {boolean} isListeningToAudioStream
|
||||
* @property {boolean} isNoiseGateEnabled
|
||||
* @property {number} lastReceivedAudioLoudness <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID <em>Read-only.</em>
|
||||
*/
|
||||
class AgentScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
||||
Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound)
|
||||
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
|
||||
Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled)
|
||||
Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness)
|
||||
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
|
||||
|
||||
public:
|
||||
AgentScriptingInterface(Agent* agent);
|
||||
|
||||
bool isPlayingAvatarSound() const { return _agent->isPlayingAvatarSound(); }
|
||||
|
||||
bool isListeningToAudioStream() const { return _agent->isListeningToAudioStream(); }
|
||||
void setIsListeningToAudioStream(bool isListeningToAudioStream) const { _agent->setIsListeningToAudioStream(isListeningToAudioStream); }
|
||||
|
||||
bool isNoiseGateEnabled() const { return _agent->isNoiseGateEnabled(); }
|
||||
void setIsNoiseGateEnabled(bool isNoiseGateEnabled) const { _agent->setIsNoiseGateEnabled(isNoiseGateEnabled); }
|
||||
|
||||
float getLastReceivedAudioLoudness() const { return _agent->getLastReceivedAudioLoudness(); }
|
||||
QUuid getSessionUUID() const { return _agent->getSessionUUID(); }
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* @function Agent.setIsAvatar
|
||||
* @param {boolean} isAvatar
|
||||
*/
|
||||
void setIsAvatar(bool isAvatar) const { _agent->setIsAvatar(isAvatar); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.isAvatar
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool isAvatar() const { return _agent->isAvatar(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.playAvatarSound
|
||||
* @param {object} avatarSound
|
||||
*/
|
||||
void playAvatarSound(SharedSoundPointer avatarSound) const { _agent->playAvatarSound(avatarSound); }
|
||||
|
||||
private:
|
||||
Agent* _agent;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_AgentScriptingInterface_h
|
|
@ -21,6 +21,7 @@
|
|||
#include <shared/QtHelpers.h>
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <Assignment.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -63,6 +64,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||
|
||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
DependencyManager::set<AnimationCacheScriptingInterface>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
||||
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
@ -42,9 +44,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Assignment Client");
|
||||
parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
|
||||
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
|
||||
for (Assignment::Type type = Assignment::FirstType;
|
||||
|
@ -97,11 +98,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
#include "AssignmentClientChildData.h"
|
||||
#include "SharedUtil.h"
|
||||
#include <QtCore/QJsonDocument>
|
||||
#ifdef _POSIX_SOURCE
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
@ -71,6 +74,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||
|
||||
adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks));
|
||||
// use QProcess to fork off a process for each of the child assignment clients
|
||||
for (unsigned int i = 0; i < _numAssignmentClientForks; i++) {
|
||||
spawnChildClient();
|
||||
|
@ -372,3 +376,27 @@ bool AssignmentClientMonitor::handleHTTPRequest(HTTPConnection* connection, cons
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::adjustOSResources(unsigned int numForks) const
|
||||
{
|
||||
#ifdef _POSIX_SOURCE
|
||||
// QProcess on Unix uses six (I think) descriptors, some temporarily, for each child proc.
|
||||
// Formula based on tests with a Ubuntu 16.04 VM.
|
||||
unsigned requiredDescriptors = 30 + 6 * numForks;
|
||||
struct rlimit descLimits;
|
||||
if (getrlimit(RLIMIT_NOFILE, &descLimits) == 0) {
|
||||
if (descLimits.rlim_cur < requiredDescriptors) {
|
||||
descLimits.rlim_cur = requiredDescriptors;
|
||||
if (setrlimit(RLIMIT_NOFILE, &descLimits) == 0) {
|
||||
qDebug() << "Resetting descriptor limit to" << requiredDescriptors;
|
||||
} else {
|
||||
const char *const errorString = strerror(errno);
|
||||
qDebug() << "Failed to reset descriptor limit to" << requiredDescriptors << ":" << errorString;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *const errorString = strerror(errno);
|
||||
qDebug() << "Failed to read descriptor limit:" << errorString;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ public slots:
|
|||
private:
|
||||
void spawnChildClient();
|
||||
void simultaneousWaitOnChildren(int waitMsecs);
|
||||
void adjustOSResources(unsigned int numForks) const;
|
||||
|
||||
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
|
||||
|
||||
|
|
|
@ -945,22 +945,14 @@ void AssetServer::sendStatsPacket() {
|
|||
upstreamStats["2. Sent Packets"] = stat.second.sentPackets;
|
||||
upstreamStats["3. Recvd ACK"] = events[Events::ReceivedACK];
|
||||
upstreamStats["4. Procd ACK"] = events[Events::ProcessedACK];
|
||||
upstreamStats["5. Recvd LACK"] = events[Events::ReceivedLightACK];
|
||||
upstreamStats["6. Recvd NAK"] = events[Events::ReceivedNAK];
|
||||
upstreamStats["7. Recvd TNAK"] = events[Events::ReceivedTimeoutNAK];
|
||||
upstreamStats["8. Sent ACK2"] = events[Events::SentACK2];
|
||||
upstreamStats["9. Retransmitted"] = events[Events::Retransmission];
|
||||
upstreamStats["5. Retransmitted"] = events[Events::Retransmission];
|
||||
nodeStats["Upstream Stats"] = upstreamStats;
|
||||
|
||||
QJsonObject downstreamStats;
|
||||
downstreamStats["1. Recvd (P/s)"] = stat.second.receiveRate;
|
||||
downstreamStats["2. Recvd Packets"] = stat.second.receivedPackets;
|
||||
downstreamStats["3. Sent ACK"] = events[Events::SentACK];
|
||||
downstreamStats["4. Sent LACK"] = events[Events::SentLightACK];
|
||||
downstreamStats["5. Sent NAK"] = events[Events::SentNAK];
|
||||
downstreamStats["6. Sent TNAK"] = events[Events::SentTimeoutNAK];
|
||||
downstreamStats["7. Recvd ACK2"] = events[Events::ReceivedACK2];
|
||||
downstreamStats["8. Duplicates"] = events[Events::Duplicate];
|
||||
downstreamStats["4. Duplicates"] = events[Events::Duplicate];
|
||||
nodeStats["Downstream Stats"] = downstreamStats;
|
||||
|
||||
QString uuid;
|
||||
|
|
|
@ -447,18 +447,21 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
|
|||
// send a kill packet for it to our other nodes
|
||||
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
|
||||
// we relay avatar kill packets to agents that are not upstream
|
||||
// and downstream avatar mixers, if the node that was just killed was being replicated
|
||||
return (node->getType() == NodeType::Agent && !node->isUpstream()) ||
|
||||
(avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node));
|
||||
// and downstream avatar mixers, if the node that was just killed was being replicatedConnectedAgent
|
||||
return node->getActiveSocket() &&
|
||||
((node->getType() == NodeType::Agent && !node->isUpstream()) ||
|
||||
(avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node)));
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
if (!killPacket) {
|
||||
killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason));
|
||||
killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||
killPacket->write(avatarNode->getUUID().toRfc4122());
|
||||
killPacket->writePrimitive(KillAvatarReason::AvatarDisconnected);
|
||||
}
|
||||
|
||||
nodeList->sendUnreliablePacket(*killPacket, *node);
|
||||
auto killPacketCopy = NLPacket::createCopy(*killPacket);
|
||||
|
||||
nodeList->sendPacket(std::move(killPacketCopy), *node);
|
||||
} else {
|
||||
// send a replicated kill packet to the downstream avatar mixer
|
||||
if (!replicatedKillPacket) {
|
||||
|
@ -643,6 +646,7 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
|
|||
auto start = usecTimestampNow();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
||||
|
||||
bool addToIgnore;
|
||||
message->readPrimitive(&addToIgnore);
|
||||
while (message->getBytesLeftToRead()) {
|
||||
|
@ -650,17 +654,22 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
|
|||
QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (nodeList->nodeWithUUID(ignoredUUID)) {
|
||||
// Reset the lastBroadcastTime for the ignored avatar to 0
|
||||
// so the AvatarMixer knows it'll have to send identity data about the ignored avatar
|
||||
// to the ignorer if the ignorer unignores.
|
||||
nodeData->setLastBroadcastTime(ignoredUUID, 0);
|
||||
if (nodeData) {
|
||||
// Reset the lastBroadcastTime for the ignored avatar to 0
|
||||
// so the AvatarMixer knows it'll have to send identity data about the ignored avatar
|
||||
// to the ignorer if the ignorer unignores.
|
||||
nodeData->setLastBroadcastTime(ignoredUUID, 0);
|
||||
}
|
||||
|
||||
|
||||
// Reset the lastBroadcastTime for the ignorer (FROM THE PERSPECTIVE OF THE IGNORED) to 0
|
||||
// so the AvatarMixer knows it'll have to send identity data about the ignorer
|
||||
// to the ignored if the ignorer unignores.
|
||||
auto ignoredNode = nodeList->nodeWithUUID(ignoredUUID);
|
||||
AvatarMixerClientData* ignoredNodeData = reinterpret_cast<AvatarMixerClientData*>(ignoredNode->getLinkedData());
|
||||
ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0);
|
||||
if (ignoredNodeData) {
|
||||
ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (addToIgnore) {
|
||||
|
|
|
@ -108,7 +108,7 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
|
|||
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
||||
if (!isRadiusIgnoring(other->getUUID())) {
|
||||
addToRadiusIgnoringSet(other->getUUID());
|
||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason));
|
||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||
killPacket->write(other->getUUID().toRfc4122());
|
||||
if (self->isIgnoreRadiusEnabled()) {
|
||||
killPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble);
|
||||
|
@ -116,7 +116,7 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe
|
|||
killPacket->writePrimitive(KillAvatarReason::YourAvatarEnteredTheirBubble);
|
||||
}
|
||||
setLastBroadcastTime(other->getUUID(), 0);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*killPacket, *self);
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(killPacket), *self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -522,11 +522,8 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityID
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID
|
||||
<< "More info:" << jsonObject;
|
||||
tree->withWriteLock([&] {
|
||||
tree->deleteEntity(entityID, true);
|
||||
});
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "EntityServer.h"
|
||||
|
||||
|
||||
EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
|
||||
OctreeSendThread(myServer, node)
|
||||
{
|
||||
|
@ -100,7 +99,7 @@ void EntityTreeSendThread::preDistributionProcessing() {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene) {
|
||||
if (viewFrustumChanged || _traversal.finished()) {
|
||||
EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
|
||||
|
@ -111,7 +110,7 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
|
||||
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
||||
|
||||
|
||||
startNewTraversal(newView, root);
|
||||
|
||||
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
|
||||
|
@ -156,7 +155,20 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
OctreeServer::trackTreeTraverseTime((float)(usecTimestampNow() - startTime));
|
||||
}
|
||||
|
||||
OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||
bool sendComplete = OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||
|
||||
if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) {
|
||||
// Dealt with all nearby entities.
|
||||
nodeData->setReportInitialCompletion(false);
|
||||
|
||||
// Send EntityQueryInitialResultsComplete reliable packet ...
|
||||
auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete,
|
||||
sizeof(OCTREE_PACKET_SEQUENCE), true);
|
||||
initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U));
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(initialCompletion), *node);
|
||||
}
|
||||
|
||||
return sendComplete;
|
||||
}
|
||||
|
||||
bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID,
|
||||
|
@ -301,6 +313,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
|
||||
bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
|
||||
if (_sendQueue.empty()) {
|
||||
params.stopReason = EncodeBitstreamParams::FINISHED;
|
||||
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
|
||||
|
||||
protected:
|
||||
void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene) override;
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -211,10 +211,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
//_totalWastedBytes += 0;
|
||||
_trueBytesSent += numBytes;
|
||||
numPackets++;
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -231,9 +230,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
|
||||
// second packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(sentPacket, *node);
|
||||
|
||||
numBytes = nodeData->getPacket().getDataSize();
|
||||
numBytes = sentPacket.getDataSize();
|
||||
_totalBytes += numBytes;
|
||||
_totalPackets++;
|
||||
// we count wasted bytes here because we were unable to fit the stats packet
|
||||
|
@ -243,8 +242,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
numPackets++;
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -265,9 +262,10 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
||||
// just send the octree packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(sentPacket, *node);
|
||||
|
||||
int numBytes = nodeData->getPacket().getDataSize();
|
||||
int numBytes = sentPacket.getDataSize();
|
||||
_totalBytes += numBytes;
|
||||
_totalPackets++;
|
||||
int thisWastedBytes = udt::MAX_PACKET_SIZE - numBytes;
|
||||
|
@ -276,8 +274,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
_trueBytesSent += numBytes;
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -434,7 +430,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
return _truePacketsSent;
|
||||
}
|
||||
|
||||
void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
bool OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
// calculate max number of packets that can be sent during this interval
|
||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
@ -517,4 +513,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
}
|
||||
|
||||
return params.stopReason == EncodeBitstreamParams::FINISHED;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ protected:
|
|||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process() override;
|
||||
|
||||
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
virtual bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene);
|
||||
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <ResourceManager.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCache.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
#include <WebSocketServerClass.h>
|
||||
|
||||
|
@ -66,6 +66,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
DependencyManager::set<ScriptCache>();
|
||||
|
@ -438,7 +439,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
|||
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
||||
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
if (NOT WINDOW_SDK_PATH)
|
||||
|
@ -52,32 +54,27 @@ endif()
|
|||
|
||||
if (ANDROID)
|
||||
# assume that the toolchain selected for android has C++11 support
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
elseif(APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
elseif ((NOT MSVC12) AND (NOT MSVC14))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
if (COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
|
||||
if (COMPILER_SUPPORTS_CXX14)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
else()
|
||||
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
endif ()
|
||||
|
||||
if (NOT ANDROID_LIB_DIR)
|
||||
set(ANDROID_LIB_DIR $ENV{ANDROID_LIB_DIR})
|
||||
|
@ -110,4 +107,4 @@ if (APPLE)
|
|||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
22
cmake/externals/json/CMakeLists.txt
vendored
Normal file
22
cmake/externals/json/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
set(EXTERNAL_NAME json)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/json_3.1.2.zip
|
||||
URL_MD5 94dbf6ea25a7569ddc0ab6e20862cf16
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "List of json include directories")
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC69.zip
|
||||
URL_MD5 e2467b08de069da7e22ec8e032435592
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC72.zip
|
||||
URL_MD5 b1d8faf9266bfbff88274a484911eb99
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -8,120 +8,224 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
function(AUTOSCRIBE_SHADER SHADER_FILE)
|
||||
# Grab include files
|
||||
foreach(includeFile ${ARGN})
|
||||
list(APPEND SHADER_INCLUDE_FILES ${includeFile})
|
||||
endforeach()
|
||||
macro(AUTOSCRIBE_SHADER)
|
||||
message(STATUS "Processing shader ${SHADER_FILE}")
|
||||
unset(SHADER_INCLUDE_FILES)
|
||||
# Grab include files
|
||||
foreach(includeFile ${ARGN})
|
||||
list(APPEND SHADER_INCLUDE_FILES ${includeFile})
|
||||
endforeach()
|
||||
|
||||
foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES})
|
||||
get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH)
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR})
|
||||
endforeach()
|
||||
foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES})
|
||||
get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH)
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR})
|
||||
endforeach()
|
||||
|
||||
#Extract the unique include shader paths
|
||||
set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
|
||||
#message(${TARGET_NAME} Hifi for includes ${INCLUDES})
|
||||
foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE})
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
|
||||
#Extract the unique include shader paths
|
||||
set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
|
||||
foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE})
|
||||
endforeach()
|
||||
|
||||
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
|
||||
#message(ready for includes ${SHADER_INCLUDES_PATHS})
|
||||
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
|
||||
#message(ready for includes ${SHADER_INCLUDES_PATHS})
|
||||
|
||||
# make the scribe include arguments
|
||||
set(SCRIBE_INCLUDES)
|
||||
foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS})
|
||||
set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/)
|
||||
endforeach()
|
||||
# make the scribe include arguments
|
||||
set(SCRIBE_INCLUDES)
|
||||
foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS})
|
||||
set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/)
|
||||
endforeach()
|
||||
|
||||
# Define the final name of the generated shader file
|
||||
get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE)
|
||||
get_filename_component(SHADER_EXT ${SHADER_FILE} EXT)
|
||||
if(SHADER_EXT STREQUAL .slv)
|
||||
set(SHADER_TYPE vert)
|
||||
elseif(${SHADER_EXT} STREQUAL .slf)
|
||||
set(SHADER_TYPE frag)
|
||||
elseif(${SHADER_EXT} STREQUAL .slg)
|
||||
set(SHADER_TYPE geom)
|
||||
endif()
|
||||
set(SHADER_TARGET ${SHADER_TARGET}_${SHADER_TYPE})
|
||||
# Define the final name of the generated shader file
|
||||
get_filename_component(SHADER_NAME ${SHADER_FILE} NAME_WE)
|
||||
get_filename_component(SHADER_EXT ${SHADER_FILE} EXT)
|
||||
if(SHADER_EXT STREQUAL .slv)
|
||||
set(SHADER_TYPE vert)
|
||||
elseif(${SHADER_EXT} STREQUAL .slf)
|
||||
set(SHADER_TYPE frag)
|
||||
elseif(${SHADER_EXT} STREQUAL .slg)
|
||||
set(SHADER_TYPE geom)
|
||||
endif()
|
||||
file(MAKE_DIRECTORY "${SHADERS_DIR}/${SHADER_LIB}")
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_LIB}/${SHADER_NAME}.${SHADER_TYPE}")
|
||||
file(TO_CMAKE_PATH "${SHADER_TARGET}" COMPILED_SHADER)
|
||||
set(REFLECTED_SHADER "${COMPILED_SHADER}.json")
|
||||
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
|
||||
set(SHADER_TARGET_HEADER ${SHADER_TARGET}.h)
|
||||
set(SHADER_TARGET_SOURCE ${SHADER_TARGET}.cpp)
|
||||
set(SCRIBE_COMMAND scribe)
|
||||
set(SCRIBE_ARGS -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
|
||||
|
||||
# Target dependant Custom rule on the SHADER_FILE
|
||||
if (APPLE)
|
||||
set(GLPROFILE MAC_GL)
|
||||
elseif (ANDROID)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
set(SCRIBE_COMMAND ${NATIVE_SCRIBE})
|
||||
elseif (UNIX)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
else ()
|
||||
set(GLPROFILE PC_GL)
|
||||
endif()
|
||||
set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE}
|
||||
COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS}
|
||||
DEPENDS ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES} ${SHADER_FILE}
|
||||
)
|
||||
# Generate the frag/vert file
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_TARGET}
|
||||
COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS}
|
||||
DEPENDS ${SHADER_FILE} ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES})
|
||||
|
||||
#output the generated file name
|
||||
set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} PARENT_SCOPE)
|
||||
# Generate the json reflection
|
||||
# FIXME move to spirv-cross for this task after we have spirv compatible shaders
|
||||
add_custom_command(
|
||||
OUTPUT ${REFLECTED_SHADER}
|
||||
COMMAND ${SHREFLECT_COMMAND} ${COMPILED_SHADER}
|
||||
DEPENDS ${SHREFLECT_DEPENDENCY} ${COMPILED_SHADER})
|
||||
|
||||
file(GLOB INCLUDE_FILES ${SHADER_TARGET_HEADER})
|
||||
#output the generated file name
|
||||
source_group("Compiled/${SHADER_LIB}" FILES ${COMPILED_SHADER})
|
||||
set_property(SOURCE ${COMPILED_SHADER} PROPERTY SKIP_AUTOMOC ON)
|
||||
list(APPEND COMPILED_SHADERS ${COMPILED_SHADER})
|
||||
|
||||
endfunction()
|
||||
source_group("Reflected/${SHADER_LIB}" FILES ${REFLECTED_SHADER})
|
||||
list(APPEND REFLECTED_SHADERS ${REFLECTED_SHADER})
|
||||
|
||||
string(CONCAT SHADER_QRC "${SHADER_QRC}" "<file alias=\"${SHADER_COUNT}\">${COMPILED_SHADER}</file>\n")
|
||||
string(CONCAT SHADER_QRC "${SHADER_QRC}" "<file alias=\"${SHADER_COUNT}_reflection\">${REFLECTED_SHADER}</file>\n")
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${SHADER_NAME} = ${SHADER_COUNT},\n")
|
||||
|
||||
MATH(EXPR SHADER_COUNT "${SHADER_COUNT}+1")
|
||||
endmacro()
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_LIB)
|
||||
set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "")
|
||||
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
|
||||
foreach(HIFI_LIBRARY ${ARGN})
|
||||
#if (NOT TARGET ${HIFI_LIBRARY})
|
||||
# file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}/src/)
|
||||
#endif ()
|
||||
if (NOT ("${TARGET_NAME}" STREQUAL "shaders"))
|
||||
message(FATAL_ERROR "AUTOSCRIBE_SHADER_LIB can only be used by the shaders library")
|
||||
endif()
|
||||
|
||||
#file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh)
|
||||
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
|
||||
endforeach()
|
||||
#message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}")
|
||||
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES "${CMAKE_SOURCE_DIR}/libraries/${SHADER_LIB}/src")
|
||||
string(REGEX REPLACE "[-]" "_" SHADER_NAMESPACE ${SHADER_LIB})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace ${SHADER_NAMESPACE} {\n")
|
||||
set(SRC_FOLDER "${CMAKE_SOURCE_DIR}/libraries/${ARGN}/src")
|
||||
|
||||
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
|
||||
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg)
|
||||
# Process the scribe headers
|
||||
file(GLOB_RECURSE SHADER_INCLUDE_FILES ${SRC_FOLDER}/*.slh)
|
||||
if(SHADER_INCLUDE_FILES)
|
||||
source_group("${SHADER_LIB}/Headers" FILES ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND ALL_SHADER_HEADERS ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_INCLUDE_FILES})
|
||||
endif()
|
||||
|
||||
#make the shader folder
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
file(GLOB_RECURSE SHADER_VERTEX_FILES ${SRC_FOLDER}/*.slv)
|
||||
if (SHADER_VERTEX_FILES)
|
||||
source_group("${SHADER_LIB}/Vertex" FILES ${SHADER_VERTEX_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_VERTEX_FILES})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace vertex { enum {\n")
|
||||
foreach(SHADER_FILE ${SHADER_VERTEX_FILES})
|
||||
AUTOSCRIBE_SHADER(${ALL_SHADER_HEADERS})
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // vertex \n")
|
||||
endif()
|
||||
|
||||
#message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}")
|
||||
set(AUTOSCRIBE_SHADER_SRC "")
|
||||
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
|
||||
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
|
||||
file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
|
||||
set_property(SOURCE ${AUTOSCRIBE_GENERATED_FILE} PROPERTY SKIP_AUTOMOC ON)
|
||||
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
|
||||
endforeach()
|
||||
#message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC})
|
||||
file(GLOB_RECURSE SHADER_FRAGMENT_FILES ${SRC_FOLDER}/*.slf)
|
||||
if (SHADER_FRAGMENT_FILES)
|
||||
source_group("${SHADER_LIB}/Fragment" FILES ${SHADER_FRAGMENT_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_FRAGMENT_FILES})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace fragment { enum {\n")
|
||||
foreach(SHADER_FILE ${SHADER_FRAGMENT_FILES})
|
||||
AUTOSCRIBE_SHADER(${ALL_SHADER_HEADERS})
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // fragment \n")
|
||||
endif()
|
||||
|
||||
# FIXME add support for geometry, compute and tesselation shaders
|
||||
#file(GLOB_RECURSE SHADER_GEOMETRY_FILES ${SRC_FOLDER}/*.slg)
|
||||
#file(GLOB_RECURSE SHADER_COMPUTE_FILES ${SRC_FOLDER}/*.slc)
|
||||
|
||||
if (WIN32)
|
||||
source_group("Shaders" FILES ${SHADER_INCLUDE_FILES})
|
||||
source_group("Shaders" FILES ${SHADER_SOURCE_FILES})
|
||||
source_group("Shaders\\generated" FILES ${AUTOSCRIBE_SHADER_SRC})
|
||||
endif()
|
||||
# the programs
|
||||
file(GLOB_RECURSE SHADER_PROGRAM_FILES ${SRC_FOLDER}/*.slp)
|
||||
if (SHADER_PROGRAM_FILES)
|
||||
source_group("${SHADER_LIB}/Program" FILES ${SHADER_PROGRAM_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_PROGRAM_FILES})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace program { enum {\n")
|
||||
foreach(PROGRAM_FILE ${SHADER_PROGRAM_FILES})
|
||||
get_filename_component(PROGRAM_NAME ${PROGRAM_FILE} NAME_WE)
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${PROGRAM_NAME})
|
||||
file(READ ${PROGRAM_FILE} PROGRAM_CONFIG)
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX ${PROGRAM_NAME})
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${PROGRAM_NAME})
|
||||
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_SOURCE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC})
|
||||
if (NOT("${PROGRAM_CONFIG}" STREQUAL ""))
|
||||
string(REGEX MATCH ".*VERTEX +([_\\:A-Z0-9a-z]+)" MVERT ${PROGRAM_CONFIG})
|
||||
if (CMAKE_MATCH_1)
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
string(REGEX MATCH ".*FRAGMENT +([_:A-Z0-9a-z]+)" MFRAG ${PROGRAM_CONFIG})
|
||||
if (CMAKE_MATCH_1)
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Link library shaders, if they exist
|
||||
include_directories("${SHADERS_DIR}")
|
||||
if (NOT (${AUTOSCRIBE_PROGRAM_VERTEX} MATCHES ".*::.*"))
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX "vertex::${AUTOSCRIBE_PROGRAM_VERTEX}")
|
||||
endif()
|
||||
if (NOT (${AUTOSCRIBE_PROGRAM_FRAGMENT} MATCHES ".*::.*"))
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT "fragment::${AUTOSCRIBE_PROGRAM_FRAGMENT}")
|
||||
endif()
|
||||
|
||||
# Add search directory to find gpu/Shader.h
|
||||
include_directories("${HIFI_LIBRARY_DIR}/gpu/src")
|
||||
set(PROGRAM_ENTRY "${PROGRAM_NAME} = (${AUTOSCRIBE_PROGRAM_VERTEX} << 16) | ${AUTOSCRIBE_PROGRAM_FRAGMENT},\n")
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${PROGRAM_ENTRY}")
|
||||
string(CONCAT SHADER_PROGRAMS_ARRAY "${SHADER_PROGRAMS_ARRAY} ${SHADER_NAMESPACE}::program::${PROGRAM_NAME},\n")
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // program \n")
|
||||
endif()
|
||||
|
||||
# Finish the shader enums
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "} // namespace ${SHADER_NAMESPACE}\n")
|
||||
#file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
|
||||
#foreach(HIFI_LIBRARY ${ARGN})
|
||||
#list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
|
||||
#endforeach()
|
||||
#endif()
|
||||
endmacro()
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_LIBS)
|
||||
set(SCRIBE_COMMAND scribe)
|
||||
set(SHREFLECT_COMMAND shreflect)
|
||||
set(SHREFLECT_DEPENDENCY shreflect)
|
||||
|
||||
# Target dependant Custom rule on the SHADER_FILE
|
||||
if (ANDROID)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
set(SCRIBE_COMMAND ${NATIVE_SCRIBE})
|
||||
set(SHREFLECT_COMMAND ${NATIVE_SHREFLECT})
|
||||
unset(SHREFLECT_DEPENDENCY)
|
||||
else()
|
||||
if (APPLE)
|
||||
set(GLPROFILE MAC_GL)
|
||||
elseif(UNIX)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
else()
|
||||
set(GLPROFILE PC_GL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Start the shader IDs
|
||||
set(SHADER_COUNT 1)
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders")
|
||||
set(SHADER_ENUMS "")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
|
||||
#
|
||||
# Scribe generation & program defintiion
|
||||
#
|
||||
foreach(SHADER_LIB ${ARGN})
|
||||
AUTOSCRIBE_SHADER_LIB(${SHADER_LIB})
|
||||
endforeach()
|
||||
|
||||
# Generate the library files
|
||||
configure_file(
|
||||
ShaderEnums.cpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp)
|
||||
configure_file(
|
||||
ShaderEnums.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h)
|
||||
configure_file(
|
||||
shaders.qrc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc)
|
||||
|
||||
set(AUTOSCRIBE_SHADER_LIB_SRC "${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h;${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp")
|
||||
set(QT_RESOURCES_FILE ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc)
|
||||
|
||||
# Custom targets required to force generation of the shaders via scribe
|
||||
add_custom_target(scribe_shaders SOURCES ${ALL_SCRIBE_SHADERS})
|
||||
add_custom_target(compiled_shaders SOURCES ${COMPILED_SHADERS})
|
||||
add_custom_target(reflected_shaders SOURCES ${REFLECTED_SHADERS})
|
||||
set_target_properties(scribe_shaders PROPERTIES FOLDER "Shaders")
|
||||
set_target_properties(compiled_shaders PROPERTIES FOLDER "Shaders")
|
||||
set_target_properties(reflected_shaders PROPERTIES FOLDER "Shaders")
|
||||
endmacro()
|
||||
|
|
|
@ -19,12 +19,8 @@ function(LINK_HIFI_LIBRARIES)
|
|||
endforeach()
|
||||
|
||||
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
|
||||
|
||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
|
||||
|
||||
#add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
|
||||
# link the actual library - it is static so don't bubble it up
|
||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
endforeach()
|
||||
|
|
|
@ -51,6 +51,10 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
set(USE_STABLE_GLOBAL_SERVICES 1)
|
||||
endif ()
|
||||
|
||||
if (NOT BYPASS_SIGNING)
|
||||
set(BYPASS_SIGNING 0)
|
||||
endif ()
|
||||
|
||||
elseif (RELEASE_TYPE STREQUAL "PR")
|
||||
set(DEPLOY_PACKAGE TRUE)
|
||||
set(PR_BUILD 1)
|
||||
|
|
13
cmake/macros/TargetJson.cmake
Normal file
13
cmake/macros/TargetJson.cmake
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2018/07/22
|
||||
# Copyright 2013-2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_JSON)
|
||||
add_dependency_external_projects(json)
|
||||
find_package(JSON REQUIRED)
|
||||
message("JSON_INCLUDE_DIRS ${JSON_INCLUDE_DIRS}")
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${JSON_INCLUDE_DIRS})
|
||||
endmacro()
|
19
cmake/modules/FindJSON.cmake
Normal file
19
cmake/modules/FindJSON.cmake
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2018/07/22
|
||||
# Copyright 2013-2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
# setup hints for JSON search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("json")
|
||||
|
||||
# locate header
|
||||
find_path(JSON_INCLUDE_DIRS "json/json.hpp" HINTS ${JSON_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(JSON DEFAULT_MSG JSON_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(JSON_INCLUDE_DIRS JSON_SEARCH_DIRS)
|
|
@ -50,3 +50,4 @@ set(SERVER_COMPONENT_CONDITIONAL "@SERVER_COMPONENT_CONDITIONAL@")
|
|||
set(CLIENT_COMPONENT_CONDITIONAL "@CLIENT_COMPONENT_CONDITIONAL@")
|
||||
set(INSTALLER_TYPE "@INSTALLER_TYPE@")
|
||||
set(APP_USER_MODEL_ID "@APP_USER_MODEL_ID@")
|
||||
set(BYPASS_SIGNING "@BYPASS_SIGNING@")
|
||||
|
|
|
@ -130,7 +130,11 @@
|
|||
; The Inner invocation has written an uninstaller binary for us.
|
||||
; We need to sign it if it's a production or PR build.
|
||||
!if @PRODUCTION_BUILD@ == 1
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!if @BYPASS_SIGNING@ == 1
|
||||
!warning "BYPASS_SIGNING set - installer will not be signed"
|
||||
!else
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!endif
|
||||
!endif
|
||||
|
||||
; Good. Now we can carry on writing the real installer.
|
||||
|
|
|
@ -46,6 +46,14 @@
|
|||
"default": "40102",
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "enable_packet_verification",
|
||||
"label": "Enable Packet Verification",
|
||||
"help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.",
|
||||
"default": true,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1223,7 +1231,7 @@
|
|||
"name": "max_avatar_height",
|
||||
"type": "double",
|
||||
"label": "Maximum Avatar Height (meters)",
|
||||
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1755.",
|
||||
"help": "Limits the height of avatars in your domain. Cannot be greater than 1755.",
|
||||
"placeholder": 5.2,
|
||||
"default": 5.2
|
||||
},
|
||||
|
|
|
@ -58,7 +58,11 @@ $(document).ready(function(){
|
|||
}
|
||||
|
||||
Settings.handlePostSettings = function(formJSON) {
|
||||
|
||||
|
||||
if (!verifyAvatarHeights()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we've set the basic http password
|
||||
if (formJSON["security"]) {
|
||||
|
||||
|
@ -207,7 +211,7 @@ $(document).ready(function(){
|
|||
swal({
|
||||
title: '',
|
||||
type: 'error',
|
||||
text: "There was a problem retreiving domain information from High Fidelity API.",
|
||||
text: "There was a problem retrieving domain information from High Fidelity API.",
|
||||
confirmButtonText: 'Try again',
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false
|
||||
|
@ -288,7 +292,7 @@ $(document).ready(function(){
|
|||
swal({
|
||||
title: 'Create new domain ID',
|
||||
type: 'input',
|
||||
text: 'Enter a label this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>',
|
||||
text: 'Enter a label for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Create",
|
||||
closeOnConfirm: false,
|
||||
|
@ -669,7 +673,7 @@ $(document).ready(function(){
|
|||
var spinner = createDomainSpinner();
|
||||
$('#' + Settings.PLACES_TABLE_ID).after($(spinner));
|
||||
|
||||
var errorEl = createDomainLoadingError("There was an error retreiving your places.");
|
||||
var errorEl = createDomainLoadingError("There was an error retrieving your places.");
|
||||
$("#" + Settings.PLACES_TABLE_ID).after(errorEl);
|
||||
|
||||
// do we have a domain ID?
|
||||
|
@ -1091,4 +1095,43 @@ $(document).ready(function(){
|
|||
|
||||
$('#settings_backup .panel-body').html(html);
|
||||
}
|
||||
|
||||
function verifyAvatarHeights() {
|
||||
var errorString = '';
|
||||
var minAllowedHeight = 0.009;
|
||||
var maxAllowedHeight = 1755;
|
||||
var alertCss = { backgroundColor: '#ffa0a0' };
|
||||
var minHeightElement = $('input[name="avatars.min_avatar_height"]');
|
||||
var maxHeightElement = $('input[name="avatars.max_avatar_height"]');
|
||||
|
||||
var minHeight = Number(minHeightElement.val());
|
||||
var maxHeight = Number(maxHeightElement.val());
|
||||
|
||||
if (maxHeight < minHeight) {
|
||||
errorString = 'Maximum avatar height must not be less than minimum avatar height<br>';
|
||||
minHeightElement.css(alertCss);
|
||||
maxHeightElement.css(alertCss);
|
||||
};
|
||||
if (minHeight < minAllowedHeight) {
|
||||
errorString += 'Minimum avatar height must not be less than ' + minAllowedHeight + '<br>';
|
||||
minHeightElement.css(alertCss);
|
||||
}
|
||||
if (maxHeight > maxAllowedHeight) {
|
||||
errorString += 'Maximum avatar height must not be greater than ' + maxAllowedHeight + '<br>';
|
||||
maxHeightElement.css(alertCss);
|
||||
}
|
||||
|
||||
if (errorString.length > 0) {
|
||||
swal({
|
||||
type: 'error',
|
||||
title: '',
|
||||
text: errorString,
|
||||
html: true
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -264,7 +264,8 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
QList<SharedNodePointer> nodesToKill;
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
limitedNodeList->eachNode([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){
|
||||
QWeakPointer<LimitedNodeList> limitedNodeListWeak = limitedNodeList;
|
||||
limitedNodeList->eachNode([this, limitedNodeListWeak, &nodesToKill](const SharedNodePointer& node){
|
||||
// the id and the username in NodePermissions will often be the same, but id is set before
|
||||
// authentication and verifiedUsername is only set once they user's key has been confirmed.
|
||||
QString verifiedUsername = node->getPermissions().getVerifiedUserName();
|
||||
|
@ -296,7 +297,8 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
machineFingerprint = nodeData->getMachineFingerprint();
|
||||
|
||||
auto sendingAddress = nodeData->getSendingSockAddr().getAddress();
|
||||
isLocalUser = (sendingAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||
auto nodeList = limitedNodeListWeak.lock();
|
||||
isLocalUser = ((nodeList && sendingAddress == nodeList->getLocalSockAddr().getAddress()) ||
|
||||
sendingAddress == QHostAddress::LocalHost);
|
||||
}
|
||||
|
||||
|
@ -458,7 +460,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
|
||||
// in case this is a node that's failing to connect
|
||||
// double check we don't have the same node whose sockets match exactly already in the list
|
||||
limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){
|
||||
limitedNodeList->eachNodeBreakable([nodeConnection, username, &existingNodeID](const SharedNodePointer& node){
|
||||
|
||||
if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) {
|
||||
// we have a node that already has these exact sockets - this can occur if a node
|
||||
|
@ -1009,7 +1011,7 @@ void DomainGatekeeper::refreshGroupsCache() {
|
|||
getDomainOwnerFriendsList();
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
nodeList->eachNode([this](const SharedNodePointer& node) {
|
||||
if (!node->getPermissions().isAssignment) {
|
||||
// this node is an agent
|
||||
const QString& verifiedUserName = node->getPermissions().getVerifiedUserName();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
|
@ -69,6 +70,14 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com";
|
|||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com";
|
||||
#endif
|
||||
|
||||
QString DomainServer::_iceServerAddr { ICE_SERVER_DEFAULT_HOSTNAME };
|
||||
int DomainServer::_iceServerPort { ICE_SERVER_DEFAULT_PORT };
|
||||
bool DomainServer::_overrideDomainID { false };
|
||||
QUuid DomainServer::_overridingDomainID;
|
||||
bool DomainServer::_getTempName { false };
|
||||
QString DomainServer::_userConfigFilename;
|
||||
int DomainServer::_parentPID { -1 };
|
||||
|
||||
bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||
const QString& metaversePath,
|
||||
const QString& requestSubobjectKey,
|
||||
|
@ -148,24 +157,13 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_allAssignments(),
|
||||
_unfulfilledAssignments(),
|
||||
_isUsingDTLS(false),
|
||||
_oauthProviderURL(),
|
||||
_oauthClientID(),
|
||||
_hostname(),
|
||||
_ephemeralACScripts(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_automaticNetworkingSetting(),
|
||||
_settingsManager(),
|
||||
_iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME),
|
||||
_iceServerPort(ICE_SERVER_DEFAULT_PORT)
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
if (_parentPID != -1) {
|
||||
watchParentProcess(_parentPID);
|
||||
}
|
||||
|
||||
parseCommandLine();
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
DependencyManager::set<StatTracker>();
|
||||
|
@ -185,9 +183,16 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
// (need this since domain-server can restart itself and maintain static variables)
|
||||
DependencyManager::set<AccountManager>();
|
||||
|
||||
auto args = arguments();
|
||||
|
||||
_settingsManager.setupConfigMap(args);
|
||||
// load the user config
|
||||
QString userConfigFilename;
|
||||
if (!_userConfigFilename.isEmpty()) {
|
||||
userConfigFilename = _userConfigFilename;
|
||||
} else {
|
||||
// we weren't passed a user config path
|
||||
static const QString USER_CONFIG_FILE_NAME = "config.json";
|
||||
userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME);
|
||||
}
|
||||
_settingsManager.setupConfigMap(userConfigFilename);
|
||||
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
|
@ -246,8 +251,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
|
||||
// check for the temporary name parameter
|
||||
const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
|
||||
if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
|
||||
if (_getTempName) {
|
||||
getTemporaryName();
|
||||
}
|
||||
|
||||
|
@ -316,28 +320,45 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart);
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLine() {
|
||||
void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Domain Server");
|
||||
parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT");
|
||||
parser.addOption(iceServerAddressOption);
|
||||
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid");
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid", "uuid");
|
||||
parser.addOption(domainIDOption);
|
||||
|
||||
const QCommandLineOption getTempNameOption("get-temp-name", "Request a temporary domain-name");
|
||||
parser.addOption(getTempNameOption);
|
||||
|
||||
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
|
||||
parser.addOption(masterConfigOption);
|
||||
const QCommandLineOption userConfigOption("user-config", "Pass user config file pass", "path");
|
||||
parser.addOption(userConfigOption);
|
||||
|
||||
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
|
||||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qWarning() << parser.errorText() << endl;
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (parser.isSet(helpOption)) {
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -354,7 +375,7 @@ void DomainServer::parseCommandLine() {
|
|||
|
||||
if (_iceServerAddr.isEmpty()) {
|
||||
qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,14 +385,21 @@ void DomainServer::parseCommandLine() {
|
|||
qDebug() << "domain-server ID is" << _overridingDomainID;
|
||||
}
|
||||
|
||||
if (parser.isSet(getTempNameOption)) {
|
||||
_getTempName = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(userConfigOption)) {
|
||||
_userConfigFilename = parser.value(userConfigOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(parentPIDOption)) {
|
||||
bool ok = false;
|
||||
int parentPID = parser.value(parentPIDOption).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
qDebug() << "Parent process PID is" << parentPID;
|
||||
watchParentProcess(parentPID);
|
||||
_parentPID = parentPID;
|
||||
qDebug() << "Parent process PID is" << _parentPID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -630,6 +658,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
|||
|
||||
void DomainServer::setupNodeListAndAssignments() {
|
||||
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
||||
static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_packet_verification";
|
||||
|
||||
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
||||
int domainServerPort = localPortValue.toInt();
|
||||
|
@ -696,6 +725,9 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
}
|
||||
}
|
||||
|
||||
bool isAuthEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_PACKET_AUTHENTICATION).toBool();
|
||||
nodeList->setAuthenticatePackets(isAuthEnabled);
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
||||
|
@ -1046,7 +1078,7 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
|
|||
unsigned int DomainServer::countConnectedUsers() {
|
||||
unsigned int result = 0;
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
nodeList->eachNode([&result](const SharedNodePointer& node){
|
||||
// only count unassigned agents (i.e., users)
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
@ -1133,7 +1165,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
extendedHeaderStream << node->getUUID();
|
||||
extendedHeaderStream << node->getLocalID();
|
||||
extendedHeaderStream << node->getPermissions();
|
||||
|
||||
extendedHeaderStream << limitedNodeList->getAuthenticatePackets();
|
||||
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
|
||||
|
||||
// always send the node their own UUID back
|
||||
|
@ -1149,7 +1181,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
if (nodeData->isAuthenticated()) {
|
||||
// if this authenticated node has any interest types, send back those nodes as well
|
||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
limitedNodeList->eachNode([this, node, &domainListPackets, &domainListStream](const SharedNodePointer& otherNode) {
|
||||
if (otherNode->getUUID() != node->getUUID() && isInInterestSet(node, otherNode)) {
|
||||
// since we're about to add a node to the packet we start a segment
|
||||
domainListPackets->startSegment();
|
||||
|
@ -1198,6 +1230,7 @@ QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, con
|
|||
void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QWeakPointer<LimitedNodeList> limitedNodeListWeak = limitedNodeList;
|
||||
|
||||
auto addNodePacket = NLPacket::create(PacketType::DomainServerAddedNode);
|
||||
|
||||
|
@ -1209,7 +1242,7 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
|||
int connectionSecretIndex = addNodePacket->pos();
|
||||
|
||||
limitedNodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
[this, addedNode](const SharedNodePointer& node)->bool {
|
||||
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
|
||||
// is the added Node in this node's interest list?
|
||||
return isInInterestSet(node, addedNode);
|
||||
|
@ -1217,16 +1250,19 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
|||
return false;
|
||||
}
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
addNodePacket->seek(connectionSecretIndex);
|
||||
|
||||
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
|
||||
|
||||
// replace the bytes at the end of the packet for the connection secret between these nodes
|
||||
addNodePacket->write(rfcConnectionSecret);
|
||||
|
||||
[this, &addNodePacket, connectionSecretIndex, addedNode, limitedNodeListWeak](const SharedNodePointer& node) {
|
||||
// send off this packet to the node
|
||||
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
|
||||
auto limitedNodeList = limitedNodeListWeak.lock();
|
||||
if (limitedNodeList) {
|
||||
addNodePacket->seek(connectionSecretIndex);
|
||||
|
||||
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
|
||||
|
||||
// replace the bytes at the end of the packet for the connection secret between these nodes
|
||||
addNodePacket->write(rfcConnectionSecret);
|
||||
|
||||
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1916,14 +1952,16 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
// don't handle if we don't have a matching node
|
||||
if (!matchingNode) {
|
||||
return false;
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||
|
||||
// don't handle if we don't have node data for this node
|
||||
if (!nodeData) {
|
||||
return false;
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID());
|
||||
|
@ -1944,7 +1982,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
// request not handled
|
||||
return false;
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if this is a request for our domain ID
|
||||
|
@ -2400,8 +2439,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
|
||||
const QString ALL_NODE_DELETE_REGEX_STRING = QString("\\%1\\/?$").arg(URI_NODES);
|
||||
const QString NODE_DELETE_REGEX_STRING = QString("\\%1\\/(%2)\\/$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
const QString ALL_NODE_DELETE_REGEX_STRING = QString("%1/?$").arg(URI_NODES);
|
||||
const QString NODE_DELETE_REGEX_STRING = QString("%1/(%2)$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
|
||||
QRegExp allNodesDeleteRegex(ALL_NODE_DELETE_REGEX_STRING);
|
||||
QRegExp nodeDeleteRegex(NODE_DELETE_REGEX_STRING);
|
||||
|
@ -2829,7 +2868,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
auto serversSettings = replicationSettings.value(serversKey).toList();
|
||||
|
||||
std::vector<HifiSockAddr> knownReplicationNodes;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
nodeList->eachNode([direction, &knownReplicationNodes](const SharedNodePointer& otherNode) {
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
knownReplicationNodes.push_back(otherNode->getPublicSocket());
|
||||
|
@ -2867,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
// collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and
|
||||
// we cannot recursively take the write lock required by handleKillNode)
|
||||
std::vector<SharedNodePointer> nodesToKill;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
nodeList->eachNode([this, direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
DomainServer(int argc, char* argv[]);
|
||||
~DomainServer();
|
||||
|
||||
static void parseCommandLine(int argc, char* argv[]);
|
||||
|
||||
enum DomainType {
|
||||
NonMetaverse,
|
||||
MetaverseDomain,
|
||||
|
@ -138,7 +140,6 @@ signals:
|
|||
|
||||
private:
|
||||
QUuid getID();
|
||||
void parseCommandLine();
|
||||
|
||||
QString getContentBackupDir();
|
||||
QString getEntitiesDirPath();
|
||||
|
@ -228,7 +229,7 @@ private:
|
|||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||
TransactionHash _pendingAssignmentCredits;
|
||||
|
||||
bool _isUsingDTLS;
|
||||
bool _isUsingDTLS { false };
|
||||
|
||||
QUrl _oauthProviderURL;
|
||||
QString _oauthClientID;
|
||||
|
@ -265,10 +266,13 @@ private:
|
|||
friend class DomainGatekeeper;
|
||||
friend class DomainMetadata;
|
||||
|
||||
QString _iceServerAddr;
|
||||
int _iceServerPort;
|
||||
bool _overrideDomainID { false }; // should we override the domain-id from settings?
|
||||
QUuid _overridingDomainID { QUuid() }; // what should we override it with?
|
||||
static QString _iceServerAddr;
|
||||
static int _iceServerPort;
|
||||
static bool _overrideDomainID; // should we override the domain-id from settings?
|
||||
static QUuid _overridingDomainID; // what should we override it with?
|
||||
static bool _getTempName;
|
||||
static QString _userConfigFilename;
|
||||
static int _parentPID;
|
||||
|
||||
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||
|
|
|
@ -191,13 +191,12 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
|||
nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilename) {
|
||||
// since we're called from the DomainServerSettingsManager constructor, we don't take a write lock here
|
||||
// even though we change the underlying config map
|
||||
|
||||
_argumentList = argumentList;
|
||||
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.setUserConfigFilename(userConfigFilename);
|
||||
_configMap.loadConfig();
|
||||
|
||||
static const auto VERSION_SETTINGS_KEYPATH = "version";
|
||||
QVariant* versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH);
|
||||
|
@ -1736,7 +1735,7 @@ void DomainServerSettingsManager::persistToFile() {
|
|||
// failed to write, reload whatever the current config state is
|
||||
// with a write lock since we're about to overwrite the config map
|
||||
QWriteLocker locker(&_settingsLock);
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.loadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
DomainServerSettingsManager();
|
||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
void setupConfigMap(const QStringList& argumentList);
|
||||
void setupConfigMap(const QString& userConfigFilename);
|
||||
|
||||
// each of the three methods in this group takes a read lock of _settingsLock
|
||||
// and cannot be called when the a write lock is held by the same thread
|
||||
|
@ -144,8 +144,6 @@ private slots:
|
|||
void processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
QStringList _argumentList;
|
||||
|
||||
QJsonArray filteredDescriptionArray(bool isContentSettings);
|
||||
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||
const QJsonObject& settingDescription);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
int main(int argc, char* argv[]) {
|
||||
setupHifiApplication(BuildInfo::DOMAIN_SERVER_NAME);
|
||||
|
||||
DomainServer::parseCommandLine(argc, argv);
|
||||
|
||||
Setting::init();
|
||||
|
||||
int currentExitCode = 0;
|
||||
|
|
|
@ -214,6 +214,7 @@ link_hifi_libraries(
|
|||
controllers plugins image trackers
|
||||
ui-plugins display-plugins input-plugins
|
||||
${PLATFORM_GL_BACKEND}
|
||||
shaders
|
||||
)
|
||||
|
||||
# include the binary directory of render-utils for shader includes
|
||||
|
|
Binary file not shown.
BIN
interface/resources/avatar/animations/jog_fwd.fbx
Normal file
BIN
interface/resources/avatar/animations/jog_fwd.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/jog_left.fbx
Normal file
BIN
interface/resources/avatar/animations/jog_left.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/settle_to_idle.fbx
Normal file
BIN
interface/resources/avatar/animations/settle_to_idle.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/side_step_left_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/side_step_left_fast.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/side_step_left_fast02.fbx
Normal file
BIN
interface/resources/avatar/animations/side_step_left_fast02.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/side_strafe_left.fbx
Normal file
BIN
interface/resources/avatar/animations/side_strafe_left.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/side_strafe_left02.fbx
Normal file
BIN
interface/resources/avatar/animations/side_strafe_left02.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/walk_bwd_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_bwd_fast.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/walk_fwd_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_fwd_fast.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/walk_fwd_fast02.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_fwd_fast02.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/walk_fwd_fast_armout.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_fwd_fast_armout.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/walk_left.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_left.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/walk_left_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_left_fast.fbx
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -380,15 +380,21 @@
|
|||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011327743530273,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"age": 321.8835144042969,
|
||||
"ageAsText": "0 hours 5 minutes 21 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
|
@ -406,24 +412,36 @@
|
|||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.20154684782028198,
|
||||
"y": 0.03644842654466629,
|
||||
"z": -0.2641940414905548
|
||||
"blue": -0.03950843587517738,
|
||||
"green": 0.20785385370254517,
|
||||
"red": -0.04381325840950012,
|
||||
"x": -0.04381325840950012,
|
||||
"y": 0.20785385370254517,
|
||||
"z": -0.03950843587517738
|
||||
},
|
||||
"center": {
|
||||
"x": -0.030000001192092896,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
"blue": 0,
|
||||
"green": 0.23000000417232513,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0.23000000417232513,
|
||||
"z": 0
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.3430936932563782,
|
||||
"y": 0.18709957599639893,
|
||||
"z": 0.38838762044906616
|
||||
"blue": 0.07901687175035477,
|
||||
"green": 0.044292300939559937,
|
||||
"red": 0.08762651681900024,
|
||||
"x": 0.08762651681900024,
|
||||
"y": 0.044292300939559937,
|
||||
"z": 0.07901687175035477
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.1415468454360962,
|
||||
"y": 0.22354799509048462,
|
||||
"z": 0.12419357895851135
|
||||
"blue": 0.03950843587517738,
|
||||
"green": 0.2521461546421051,
|
||||
"red": 0.04381325840950012,
|
||||
"x": 0.04381325840950012,
|
||||
"y": 0.2521461546421051,
|
||||
"z": 0.03950843587517738
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
|
@ -441,189 +459,14 @@
|
|||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.33466479182243347,
|
||||
"y": 0.16981728374958038,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 19,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{6b0a2b08-e8e3-4d43-95cc-dfc4f7a4b0c9}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Fedora",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032827319,
|
||||
"lastEditedBy": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
|
||||
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"x": 0.2765824794769287,
|
||||
"y": 0.14034485816955566,
|
||||
"z": 0.320981502532959
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0.000143393874168396,
|
||||
"y": 1.7460365295410156,
|
||||
"z": 0.022502630949020386
|
||||
},
|
||||
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentJointIndex": 64,
|
||||
"position": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.6202316284179688,
|
||||
"x": -0.5601736903190613,
|
||||
"y": -10.668098449707031,
|
||||
"z": -0.8933582305908203
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 327680,
|
||||
"verticesCount": 719
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011027336120605,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.04381517320871353,
|
||||
"y": 0.20789726078510284,
|
||||
"z": -0.0394962802529335
|
||||
},
|
||||
"center": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.08762653172016144,
|
||||
"y": 0.04429228603839874,
|
||||
"z": 0.07901687920093536
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.043811358511447906,
|
||||
"y": 0.2521895468235016,
|
||||
"z": 0.03952059894800186
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"created": "2018-07-26T23:56:46Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"blue": 0.07229919731616974,
|
||||
"green": 0.06644226610660553,
|
||||
"red": 0.03022606298327446,
|
||||
"x": 0.03022606298327446,
|
||||
"y": 0.06644226610660553,
|
||||
"z": 0.07229919731616974
|
||||
|
@ -633,12 +476,15 @@
|
|||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{d018c6ea-b2f4-441e-85e1-d17373ae6f34}",
|
||||
"id": "{03053239-bb37-4c51-a013-a1772baaeed5}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
|
@ -653,51 +499,66 @@
|
|||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032505220,
|
||||
"lastEditedBy": "{b46f9c9e-4cd3-4964-96d6-cf3954abb908}",
|
||||
"lastEdited": 1532649569894305,
|
||||
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
"blue": 0,
|
||||
"green": 0.23000000417232513,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0.23000000417232513,
|
||||
"z": 0
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
"w": 0.5910986065864563,
|
||||
"x": -0.48726415634155273,
|
||||
"y": -0.4088630974292755,
|
||||
"z": 0.49599072337150574
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90",
|
||||
"modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx",
|
||||
"name": "Scifi Watch by Jimi",
|
||||
"naturalDimensions": {
|
||||
"blue": 0.055614765733480453,
|
||||
"green": 0.0511094331741333,
|
||||
"red": 0.023250818252563477,
|
||||
"x": 0.023250818252563477,
|
||||
"y": 0.0511094331741333,
|
||||
"z": 0.055614765733480453
|
||||
},
|
||||
"naturalPosition": {
|
||||
"blue": -0.06031447649002075,
|
||||
"green": 1.4500460624694824,
|
||||
"red": 0.6493338942527771,
|
||||
"x": 0.6493338942527771,
|
||||
"y": 1.4500460624694824,
|
||||
"z": -0.06031447649002075
|
||||
},
|
||||
"originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentJointIndex": 16,
|
||||
"position": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
"blue": 0,
|
||||
"green": 0.23000000417232513,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0.23000000417232513,
|
||||
"z": 0
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.3082179129123688,
|
||||
"x": -0.19203892350196838,
|
||||
"y": -10.429610252380371,
|
||||
"z": -0.4076632857322693
|
||||
"x": 495.7716979980469,
|
||||
"y": 498.345703125,
|
||||
"z": 498.52044677734375
|
||||
},
|
||||
"registrationPoint": {
|
||||
"blue": 0.5,
|
||||
"green": 0.5,
|
||||
"red": 0.5,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
|
@ -712,10 +573,10 @@
|
|||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
"w": 0.5910986065864563,
|
||||
"x": -0.48726415634155273,
|
||||
"y": -0.4088630974292755,
|
||||
"z": 0.49599072337150574
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
|
@ -726,6 +587,229 @@
|
|||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 308.8044128417969,
|
||||
"ageAsText": "0 hours 5 minutes 8 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"blue": -0.2340194433927536,
|
||||
"green": -0.07067721337080002,
|
||||
"red": -0.17002610862255096,
|
||||
"x": -0.17002610862255096,
|
||||
"y": -0.07067721337080002,
|
||||
"z": -0.2340194433927536
|
||||
},
|
||||
"center": {
|
||||
"blue": -0.039825439453125,
|
||||
"green": 0.02001953125,
|
||||
"red": 0.0001678466796875,
|
||||
"x": 0.0001678466796875,
|
||||
"y": 0.02001953125,
|
||||
"z": -0.039825439453125
|
||||
},
|
||||
"dimensions": {
|
||||
"blue": 0.3883880078792572,
|
||||
"green": 0.18139348924160004,
|
||||
"red": 0.34038791060447693,
|
||||
"x": 0.34038791060447693,
|
||||
"y": 0.18139348924160004,
|
||||
"z": 0.3883880078792572
|
||||
},
|
||||
"tfl": {
|
||||
"blue": 0.1543685644865036,
|
||||
"green": 0.11071627587080002,
|
||||
"red": 0.17036180198192596,
|
||||
"x": 0.17036180198192596,
|
||||
"y": 0.11071627587080002,
|
||||
"z": 0.1543685644865036
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-07-26T23:56:46Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"blue": 0.38838762044906616,
|
||||
"green": 0.16981728374958038,
|
||||
"red": 0.33466479182243347,
|
||||
"x": 0.33466479182243347,
|
||||
"y": 0.16981728374958038,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 18,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{1bf231ce-3913-4c53-be3c-b1f4094dac51}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Fedora",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1532649698129709,
|
||||
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"blue": -0.039825439453125,
|
||||
"green": 0.02001953125,
|
||||
"red": 0.0001678466796875,
|
||||
"x": 0.0001678466796875,
|
||||
"y": 0.02001953125,
|
||||
"z": -0.039825439453125
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9998477101325989,
|
||||
"x": -9.898545982878204e-09,
|
||||
"y": 5.670873406415922e-07,
|
||||
"z": 0.017452405765652657
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
|
||||
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"blue": 0.320981502532959,
|
||||
"green": 0.14034485816955566,
|
||||
"red": 0.2765824794769287,
|
||||
"x": 0.2765824794769287,
|
||||
"y": 0.14034485816955566,
|
||||
"z": 0.320981502532959
|
||||
},
|
||||
"naturalPosition": {
|
||||
"blue": 0.022502630949020386,
|
||||
"green": 1.7460365295410156,
|
||||
"red": 0.000143393874168396,
|
||||
"x": 0.000143393874168396,
|
||||
"y": 1.7460365295410156,
|
||||
"z": 0.022502630949020386
|
||||
},
|
||||
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
|
||||
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentJointIndex": 66,
|
||||
"position": {
|
||||
"blue": -0.039825439453125,
|
||||
"green": 0.02001953125,
|
||||
"red": 0.0001678466796875,
|
||||
"x": 0.0001678466796875,
|
||||
"y": 0.02001953125,
|
||||
"z": -0.039825439453125
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.6202316284179688,
|
||||
"x": 495.21051025390625,
|
||||
"y": 498.5577697753906,
|
||||
"z": 497.6370849609375
|
||||
},
|
||||
"registrationPoint": {
|
||||
"blue": 0.5,
|
||||
"green": 0.5,
|
||||
"red": 0.5,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 327680,
|
||||
"verticesCount": 719
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9998477101325989,
|
||||
"x": -9.898545982878204e-09,
|
||||
"y": 5.670873406415922e-07,
|
||||
"z": 0.017452405765652657
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
|
|
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
|
||||
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
|
||||
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
|
||||
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
|
||||
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
|
||||
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
|
||||
C19,43.3,19.6,36.4,20.9,29.8z"/>
|
||||
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
|
||||
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
|
||||
C34,59.1,32.8,60,32.3,61.4z"/>
|
||||
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
|
||||
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
|
||||
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
|
||||
</svg>
|
Before (image error) Size: 1.5 KiB |
30
interface/resources/icons/tablet-icons/emote-a.svg
Normal file
30
interface/resources/icons/tablet-icons/emote-a.svg
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M39.8,10.7c0.8,0.1,1.5,0.7,1.8,1.4s0.2,1.6-0.2,2.2c-0.3,0.4-0.2,1,0.3,1.4c0.2,0.1,0.4,0.1,0.5,0.1
|
||||
c0.3,0,0.6-0.1,0.6-0.2l0.1-0.1l0.1-0.1c0.8-1.2,1-2.8,0.4-4.1C42.8,10,41.6,9,40.1,8.8c-0.3-0.1-0.5,0-0.7,0.1
|
||||
C39.2,9,39,9.3,39,9.5C38.8,10.1,39.2,10.6,39.8,10.7z"/>
|
||||
<path d="M42.5,8.1c1.2,0.2,2.2,1,2.7,2.1c0.4,1.1,0.3,2.4-0.3,3.3c-0.2,0.2-0.2,0.5-0.2,0.7s0.2,0.5,0.5,0.7
|
||||
c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,0.8-0.5c1-1.5,1.2-3.4,0.5-5.1s-2.3-3-4.2-3.3c-0.5-0.1-1,0.2-1.1,0.8
|
||||
C41.6,7.5,41.9,8,42.5,8.1z M42.1,7L42.1,7L42.1,7L42.1,7z"/>
|
||||
<path d="M6.7,14.3c-0.4-0.6-0.5-1.5-0.2-2.2c0.3-0.8,1-1.3,1.9-1.4c0.6-0.1,0.9-0.6,0.8-1.1C9.1,9.3,9,9,8.7,8.9
|
||||
C8.5,8.8,8.3,8.7,8,8.8C6.5,9,5.3,10,4.7,11.4s-0.4,2.9,0.4,4.1l0.1,0.1l0.1,0.1c0.1,0,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.6-0.2
|
||||
C6.9,15.4,7,14.8,6.7,14.3z"/>
|
||||
<path d="M5.6,8.1C5.9,8,6.1,7.9,6.3,7.7C6.5,7.5,6.5,7.2,6.5,7C6.4,6.7,6.2,6.4,6,6.3C5.8,6.1,5.6,6.1,5.3,6.1
|
||||
C3.5,6.5,1.9,7.7,1.1,9.5c-0.7,1.7-0.5,3.6,0.5,5.1c0.1,0.3,0.4,0.5,0.8,0.5c0.1,0,0.3,0,0.6-0.2c0.2-0.2,0.4-0.4,0.4-0.6
|
||||
c0-0.3,0-0.5-0.2-0.7c-0.6-1-0.8-2.2-0.3-3.3C3.4,9.1,4.4,8.3,5.6,8.1z"/>
|
||||
<path d="M48.8,25.1c-0.7-0.7-1.8-0.7-2.6-0.2c-0.4,0.3-0.7,0.5-1,0.8L44.9,26c-0.3,0.3-0.6,0.5-0.9,0.8c-0.6,0.6-1.2,1.1-1.9,1.6
|
||||
c-1.2,0.8-2.7,1-4.1,0.5c-1.3-0.5-2.3-1.6-2.6-3c-0.1-0.6-0.2-1.3-0.2-2c-0.2-2.8-0.3-5.6-0.5-8.5l-0.3-5.2c0-0.7-0.1-1.4-0.2-2.2
|
||||
c-0.1-0.8-0.5-1.5-1.1-1.8s-1.2-0.3-1.8,0c-1,0.5-1.1,1.6-1.1,2.1c-0.1,1.4-0.2,2.8-0.2,4.2c0,0.9-0.1,1.8-0.1,2.7
|
||||
c-0.1,2.4-0.3,4.8-0.4,7.2v0.2c0,0.8-0.1,0.9-0.5,0.9s-0.4-0.1-0.6-0.9L27,16.4c-0.8-3.3-1.5-6.7-2.3-10c-0.3-1.3-1.2-1.9-2.4-1.7
|
||||
c-1.1,0.2-1.7,1.2-1.6,2.4C20.7,8,20.9,9,21,9.9c0.1,0.6,0.2,1.3,0.2,1.9c0.5,3.8,0.9,7.6,1.4,11.3l0.1,1.1
|
||||
c0.1,0.8-0.1,0.9-0.4,0.9c-0.3,0.1-0.4,0.1-0.7-0.6L20,20.7c-1.2-2.9-2.5-5.7-3.7-8.6c-0.8-1.9-2-1.9-2.8-1.5
|
||||
c-0.6,0.2-1.5,0.9-0.9,2.9c0.3,1,0.6,1.9,0.9,2.9c0.3,0.9,0.5,1.8,0.8,2.7c0.5,1.8,1,3.5,1.6,5.3l1.1,3.7c0.2,0.6,0,0.7-0.2,0.8
|
||||
c-0.1,0.1-0.3,0.1-0.7-0.4c-0.1-0.1-0.2-0.2-0.2-0.3l-3.8-5.7c-0.5-0.7-0.9-1.4-1.4-2.1c-0.6-0.8-1.4-1-2.3-0.6s-1.3,1.1-1.2,2
|
||||
c0.1,0.5,0.3,0.9,0.4,1.2c0.8,1.6,1.6,3.2,2.4,4.8c2.1,4.2,4.2,8.5,6.4,12.8c2.3,4.5,6.2,7,11.5,7.3l0,0l0,0
|
||||
c4.7-0.2,8.2-1.9,10.7-5.2c2.1-2.8,4.2-5.7,6.2-8.5c0.9-1.3,1.8-2.5,2.7-3.7c0.2-0.3,0.4-0.5,0.6-0.8c0.4-0.6,0.8-1.1,1.2-1.7
|
||||
C49.5,26.7,49.5,25.8,48.8,25.1z"/>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 2.7 KiB |
33
interface/resources/icons/tablet-icons/emote-i.svg
Normal file
33
interface/resources/icons/tablet-icons/emote-i.svg
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M39.8,10.7c0.8,0.1,1.5,0.7,1.8,1.4s0.2,1.6-0.2,2.2c-0.3,0.4-0.2,1,0.3,1.4c0.2,0.1,0.4,0.1,0.5,0.1
|
||||
c0.3,0,0.6-0.1,0.6-0.2l0.1-0.1l0.1-0.1c0.8-1.2,1-2.8,0.4-4.1C42.8,10,41.6,9,40.1,8.8c-0.3-0.1-0.5,0-0.7,0.1
|
||||
C39.2,9,39,9.3,39,9.5C38.8,10.1,39.2,10.6,39.8,10.7z"/>
|
||||
<path class="st0" d="M42.5,8.1c1.2,0.2,2.2,1,2.7,2.1c0.4,1.1,0.3,2.4-0.3,3.3c-0.2,0.2-0.2,0.5-0.2,0.7s0.2,0.5,0.5,0.7
|
||||
c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,0.8-0.5c1-1.5,1.2-3.4,0.5-5.1s-2.3-3-4.2-3.3c-0.5-0.1-1,0.2-1.1,0.8
|
||||
C41.6,7.5,41.9,8,42.5,8.1z M42.1,7L42.1,7L42.1,7L42.1,7z"/>
|
||||
<path class="st0" d="M6.7,14.3c-0.4-0.6-0.5-1.5-0.2-2.2c0.3-0.8,1-1.3,1.9-1.4c0.6-0.1,0.9-0.6,0.8-1.1C9.1,9.3,9,9,8.7,8.9
|
||||
C8.5,8.8,8.3,8.7,8,8.8C6.5,9,5.3,10,4.7,11.4s-0.4,2.9,0.4,4.1l0.1,0.1l0.1,0.1c0.1,0,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.6-0.2
|
||||
C6.9,15.4,7,14.8,6.7,14.3z"/>
|
||||
<path class="st0" d="M5.6,8.1C5.9,8,6.1,7.9,6.3,7.7C6.5,7.5,6.5,7.2,6.5,7C6.4,6.7,6.2,6.4,6,6.3C5.8,6.1,5.6,6.1,5.3,6.1
|
||||
C3.5,6.5,1.9,7.7,1.1,9.5c-0.7,1.7-0.5,3.6,0.5,5.1c0.1,0.3,0.4,0.5,0.8,0.5c0.1,0,0.3,0,0.6-0.2c0.2-0.2,0.4-0.4,0.4-0.6
|
||||
c0-0.3,0-0.5-0.2-0.7c-0.6-1-0.8-2.2-0.3-3.3C3.4,9.1,4.4,8.3,5.6,8.1z"/>
|
||||
<path class="st0" d="M48.8,25.1c-0.7-0.7-1.8-0.7-2.6-0.2c-0.4,0.3-0.7,0.5-1,0.8L44.9,26c-0.3,0.3-0.6,0.5-0.9,0.8
|
||||
c-0.6,0.6-1.2,1.1-1.9,1.6c-1.2,0.8-2.7,1-4.1,0.5c-1.3-0.5-2.3-1.6-2.6-3c-0.1-0.6-0.2-1.3-0.2-2c-0.2-2.8-0.3-5.6-0.5-8.5
|
||||
l-0.3-5.2c0-0.7-0.1-1.4-0.2-2.2c-0.1-0.8-0.5-1.5-1.1-1.8s-1.2-0.3-1.8,0c-1,0.5-1.1,1.6-1.1,2.1c-0.1,1.4-0.2,2.8-0.2,4.2
|
||||
c0,0.9-0.1,1.8-0.1,2.7c-0.1,2.4-0.3,4.8-0.4,7.2v0.2c0,0.8-0.1,0.9-0.5,0.9s-0.4-0.1-0.6-0.9L27,16.4c-0.8-3.3-1.5-6.7-2.3-10
|
||||
c-0.3-1.3-1.2-1.9-2.4-1.7c-1.1,0.2-1.7,1.2-1.6,2.4C20.7,8,20.9,9,21,9.9c0.1,0.6,0.2,1.3,0.2,1.9c0.5,3.8,0.9,7.6,1.4,11.3
|
||||
l0.1,1.1c0.1,0.8-0.1,0.9-0.4,0.9c-0.3,0.1-0.4,0.1-0.7-0.6L20,20.7c-1.2-2.9-2.5-5.7-3.7-8.6c-0.8-1.9-2-1.9-2.8-1.5
|
||||
c-0.6,0.2-1.5,0.9-0.9,2.9c0.3,1,0.6,1.9,0.9,2.9c0.3,0.9,0.5,1.8,0.8,2.7c0.5,1.8,1,3.5,1.6,5.3l1.1,3.7c0.2,0.6,0,0.7-0.2,0.8
|
||||
c-0.1,0.1-0.3,0.1-0.7-0.4c-0.1-0.1-0.2-0.2-0.2-0.3l-3.8-5.7c-0.5-0.7-0.9-1.4-1.4-2.1c-0.6-0.8-1.4-1-2.3-0.6s-1.3,1.1-1.2,2
|
||||
c0.1,0.5,0.3,0.9,0.4,1.2c0.8,1.6,1.6,3.2,2.4,4.8c2.1,4.2,4.2,8.5,6.4,12.8c2.3,4.5,6.2,7,11.5,7.3l0,0l0,0
|
||||
c4.7-0.2,8.2-1.9,10.7-5.2c2.1-2.8,4.2-5.7,6.2-8.5c0.9-1.3,1.8-2.5,2.7-3.7c0.2-0.3,0.4-0.5,0.6-0.8c0.4-0.6,0.8-1.1,1.2-1.7
|
||||
C49.5,26.7,49.5,25.8,48.8,25.1z"/>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 2.8 KiB |
Binary file not shown.
Before (image error) Size: 126 KiB After (image error) Size: 125 KiB |
Binary file not shown.
Before ![]() (image error) Size: 1.5 KiB |
Binary file not shown.
Before ![]() (image error) Size: 1.5 KiB |
|
@ -26,6 +26,7 @@ ScrollingWindow {
|
|||
y: 100
|
||||
|
||||
Component.onCompleted: {
|
||||
focus = true
|
||||
shown = true
|
||||
addressBar.text = webview.url
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ Windows.ScrollingWindow {
|
|||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
|
|
@ -173,6 +173,21 @@ Item {
|
|||
StatText {
|
||||
text: "Yaw: " + root.yaw.toFixed(1)
|
||||
}
|
||||
StatText {
|
||||
visible: root.animStackNames.length > 0;
|
||||
text: "Anim Stack Names:"
|
||||
}
|
||||
ListView {
|
||||
width: geoCol.width
|
||||
height: root.animStackNames.length * 15
|
||||
visible: root.animStackNames.length > 0;
|
||||
model: root.animStackNames
|
||||
delegate: StatText {
|
||||
text: modelData.length > 30
|
||||
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
|
||||
: modelData
|
||||
}
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " +
|
||||
|
|
|
@ -20,8 +20,10 @@ Original.Button {
|
|||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property int fontSize: hifi.fontSizes.buttonLabel
|
||||
property int radius: hifi.buttons.radius
|
||||
property alias implicitTextWidth: buttonText.implicitWidth
|
||||
property string buttonGlyph: "";
|
||||
property int fontCapitalization: Font.AllUppercase
|
||||
|
||||
width: hifi.dimensions.buttonWidth
|
||||
height: hifi.dimensions.controlLineHeight
|
||||
|
@ -45,7 +47,7 @@ Original.Button {
|
|||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: hifi.buttons.radius
|
||||
radius: control.radius
|
||||
|
||||
border.width: (control.color === hifi.buttons.none ||
|
||||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
|
||||
|
@ -107,7 +109,7 @@ Original.Button {
|
|||
RalewayBold {
|
||||
id: buttonText;
|
||||
anchors.centerIn: parent;
|
||||
font.capitalization: Font.AllUppercase
|
||||
font.capitalization: control.fontCapitalization
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||
size: control.fontSize
|
||||
|
|
|
@ -124,6 +124,11 @@ SpinBox {
|
|||
color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||
}
|
||||
}
|
||||
up.onPressedChanged: {
|
||||
if(value) {
|
||||
spinBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
down.indicator: Item {
|
||||
x: spinBox.width - implicitWidth - 5
|
||||
|
@ -138,6 +143,11 @@ SpinBox {
|
|||
color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||
}
|
||||
}
|
||||
down.onPressedChanged: {
|
||||
if(value) {
|
||||
spinBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: spinBoxLabel
|
||||
|
|
|
@ -471,7 +471,6 @@ TabletModalWindow {
|
|||
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
|
||||
}
|
||||
headerVisible: !selectDirectory
|
||||
onClicked: navigateToRow(row);
|
||||
onDoubleClicked: navigateToRow(row);
|
||||
focus: true
|
||||
Keys.onReturnPressed: navigateToCurrentRow();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue